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

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.

Related

Why is my instance variable being overwritten inside my Rails controller action?

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.

railstutorial: I have create my first association (users & posts), but can access posts

After creating an association between users and microposts, we are asked to show the first post in each user show method. Then I do that (in users controller.rb)
def show
#microposts = User.first.microposts.first
end
In rails console the query give me back the correct result, but I can't get it in the browser. What am I doing wrong?
The current result is user info instead of first post.
You are returning the first post of the first user, not the first post of the user you are showing. Try the following:
def show
#user = User.find params[:id]
#microposts = #user.microposts.first
end
However, it is not a good practice to use more instance variables than #user in your controller, I mean, the view should only have #user available instead of both #user and #microposts.

Rails 4 - Couldn't find User without an ID

I'm new to rails, so any explanation & advise would much appreciated.
i have a webpage in which i would like any user to view that page not just the current_user, but i am unsure how to correctly define the instance variable #user in my controller
in my static_pages_controller.rb i have the below action recruiterpg
static_pages_controller.rb
def recruiterpg
#user = User.find(params[:user_id])
#adverts = #user.adverts
#applications = #user.forms
end
in my controller, i have defined user as #user = User.find(params[:user_id]) but this breaks my code in the views; views/static_pages/recruiterpg.html.erb
when i define user as #user = current_user my code in the views works perfectly fine
what am trying to do is: for my views, the recruiterpg.html.erb, i would like
any user to be able to view the page not only the current_user to
view the page. Could one kindly advise me and explain to me how to
define #user correctly in my status_pages_controller.rb. i also
tried #user = User.find(params[:id]) but my code still breaks in the
views - i get the error message
Couldn't find User without an ID
You need to make sure you are passing a user_id to the recruiterpg action. For example, try this url in your browser (set user_id to a known id in the users table):
http://localhost:3000/dashboard?user_id=1
A suggested modification to your action:
def recruiterpg
#user = User.find params.require(:user_id)
#adverts = #user.adverts
#applications = #user.forms
end
If params[:user_id] isn't defined, you want to find a way to make visible what is being defined.
If you throw the following statements into your controller...
def recruiterpg
...
puts params
...
end
...you should see something like the following get spit out in your console when you load the page...
{"controller"=>"static_pages", "action"=>"recruiterpg", "id"=>"49"}
Take a look at the Rails guide for parameters. They can get defined in one of three ways.
One: As a query string similar to Sean's answer above.
Two: Routing parameters. See section 4.3 in the Rails guide. In your case, that would mean you should have something like the following in routes.rb:
get '/dashboard/:user_id' => 'staticpages#recruiterpg'
Note that there's nothing magic about :user_id in that string.
Three: From a form which it doesn't seem like applies here, since a user isn't submitting data.
Since you're new, here is some information for you:
User Story
Firstly, the best way to resolve errors is to identify your user story.
A "user story" is a software principle in which you put the "user's perspective" first -- explaining how the software should work in conditions defined from how the user engages with it.
One of the main issues you have with your question is your user story is very weak; it's hard to decifer what you're trying to achieve.
Next time you ask a question, you should try your hardest to describe how the user should see your app, before providing code snippets :)
Controller
Your main issue is an antipattern.
An antipattern is basically a "hack" which will likely break another part of your app in future. Kind of like duct tape for software):
#app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def recruiterpg
#user = User.find(params[:user_id])
#adverts = #user.adverts
#applications = #user.forms
end
end
So you're showing a static page but yet you want to populate it with data?
Hmm...
What you should be doing is something like the following:
#config/routes.rb
resources :users do
resources :recruiters, only: :index #-> url.com/users/:user_id/recruiters
end
#app/controllers/recruiters_controller.rb
class RecruitersController < ApplicationController
def index
#user = User.find params[:user_id]
#adverts = #user.adverts
#applications = #user.forms
end
end
This will allow you to populate the following view:
#app/views/recruiters/index.html.erb
<%= #adverts %>
--
It's important to note the structure of the controller / routes here.
The issue you have is that you're calling a "static page" and expecting to have params available to find a User. This can only happen if you have params available...
Params
Rails is not magic, and as such if you want to look up a user, you have to provide the parameters to do so.
This is why you're able to look up current_user -- the params are already set for this user.
As such, you'll need to use something called nested routes in order to attain a user ID other than that of current_user:
#config/routes.rb
resources :users do
resources :recruiters #-> url.com/users/:user_id/recruiters
end

Rails - Partial in another view can't access its controller

I'm trying to build a profile page that displays posts sent only to the requested user, and allows the visitor to write a post of their own. Because this simplified example should have two distinct controllers: users and posts, I made partials for each post action to render within the user's show action.
Directory structure for my views directory looks like this:
- posts
- _index.html.erb
- _new.html.erb
- users
- show.html.erb
... (etc.)
Section that displays these partials within the user's show.html.erb:
<section>
<h3>Posts:</h3>
<%= render '/posts/new', :post => Post.new %>
<%= render '/posts/index', :posts => Post.where(target_id: params[:id]) %>
</section>
I eventually found out that you could pass variables into the partial in this render line, and though this works, it's very messy and probably doesn't follow the best practices.
Ideally, I'd want these partials to be connected with the posts controller so I can write more complex database queries in a place that isn't the view:
class PostsController < ApplicationController
def new
#post = Post.new
end
def index
#posts = Post.where(target_id: params[:id])
end
def create
#post = Post.new(post_params)
#post.user_id = current_user.id
#post.target_id = params[:post][:target_id]
if #post.save
redirect_to :back, notice: 'You published a post!'
else
render new
end
end
private
def post_params
params.require(:post).permit(:body)
end
end
Currently, I haven't found a way of doing this. I know this is a newb question, but thanks for any help in advance.
You are attempting to treat your controllers like models: doing the post work in post controller and the user work in user controller. But controllers are task-oriented, not model-oriented.
Since you want posts info in your user form, it's typical to gather it in the user controller. E.g.
class UsersController < ApplicationController
def show
...
#posts = Post.where(user_id: user.id)
end
end
That #posts instance variable is visible in the show template and any partials it calls. But many coders prefer to send it explicitly through render arguments, as more functional:
<%= render '/posts/post_list', posts: #posts %>
For one thing it's easier to refactor when you can see at a glance all of the partial's dependencies.
I agree somewhat with #Mori's advice. As he said, you are trying to put too much logic into the controller. I think this was a result of you trying to get it out of the view, which is the right idea, but you want business logic to be in the model.
Also, those index and new actions for PostsController are never going to be called. When you are calling the render posts/new for example, that is rendering the view, not the controller action. So, those controller actions have no reason to exist.
I would implement the fix in perhaps a different way than Mori described. It's a recommended practice to try and pass as few instance variables from the controller to the view as possible (see 3rd bullet in the linked section).
Since it's really the show action of the UsersController we are talking about here, I as someone trying to understand your code would assume the instance variable you are passing to the show view is something like #user.
You may want to use an includes method when instantiating the #user object. The includes statement will allow you to load the additional models you will need to instantiate using the minimum number of queries possible (preventing an N+1 query situation). You probably don't want to load every single one if there are thousands of matching posts, so I put an arbitrary limit of 10 on that.
UsersController
def show
#user = User.find(params[:id]).includes(:received_posts).limit(10)
end
#....
View
<section>
<h3>Posts:</h3>
<% unless #user.id == current_user.id %>
<%= render 'posts/form', post: Post.new(user_id: #user.id) %>
<% end %>
<%= render #user.received_posts %>
</section>
Putting the partial for a new post instead as a view called posts/form will allow you to reuse that form if you want to render an edit action (form_for knows which action to use on submit by calling the passed model's persisted? method).
Note that this code assumes the User model has the second relationship with posts set up to be called received_posts, but you can change it to whatever reflects the reality. By passing the received_posts collection to the render method, Rails is smart enough to know that if you want to render a collection of Post models to look for a posts/_post partial and render one for each Post. It's a little cleaner looking IMO. Just make sure to move your posts/show code into that. posts/show implies this is its own action and not something used as a partial for something else.

Ruby / Rails - AJAX pagination of nested resources - How do I determine the parent resource?

My model has Posts, Users, and Comments. Users can leave Comments on/about Posts.
Every Comment belongs to a User and a Post.
Therefore, the Comment model has a user_id field and a post_id field.
When viewing a Post, I want to paginate through that Post's comments.
When viewing a User, I want to paginate through that User's comments.
I want to paginate using AJAX (via the Kaminari gem).
I have my nested routes set up for both.
On the Post, the URL being hit is http://localhost:3000/posts/{:id}/comments?page={page_number}
On the User, the URL being hit is http://localhost:3000/users/{:id}/comments?page={page_number}
Both URLs are hitting the index action of the Comments controller.
My question is this: inside the index action, how do I determine if the {:id} provided is a user_id or a post_id so I can retrieve the desired comments.
Check for params[:user_id] and params[:post_id] in your Comments controller:
if params[:user_id]
#call came from /users/ url
elsif params[:post_id]
#call came from /posts/ url
else
#call came from some other url
end
I like the Ryan Bates' way
class CommentsController
before_action :load_commentable
def index
#comments = #commentable.comments.page(params[:page])
end
private
def load_commentable
klass = [Post, User].detect { |c| params["#{c.name.underscore}_id"] }
#commentable = klass.find(params["#{klass.name.underscore}_id"])
end
end

Resources