How to show all my posts liked by user - ruby-on-rails

I'm trying to show all the posts that I like by current user.
I'm using Ruby on Rails, and the gems Devise and "Acts As Votable". I've been following the Acts As Votable guide but can't make it work.
I think this is the way to go:
#post.liked_by #user1
#post.downvote_from #user2
I created a new controller called dashboard:
class DashboardController < ApplicationController
def index
#post = Post.all.liked_by current_user
end
end
But when I run my web I get:
undefined method `liked_by' for #<Post::ActiveRecord_Relation:0x9aa89b0>
What can I do?

The issue is that you're calling it on an ActiveRecord Relation, not on the model object, itself. A couple of minor changes, and you'll be all set.
First, we make the instance variable plural to show that it's receiving multiple records (a user can like multiple posts):
class DashboardController < ApplicationController
def index
#posts = Post.all.liked_by current_user
end
end
Then, to process these, you'll want to navigate to the individual record or records. If you want to process the whole list, you can do this:
#posts.each do |post|
post.liked_by #user1
post.downvote_from #user2
end
This will apply the like and downvote to all of the posts. However, you can update only the first post, like this:
post = #posts.first
post.liked_by #user1
post.downvote_from #user2
If you already knew what post you wanted to vote on (maybe based on which was chosen in the UI), you could do this:
#post = Post.find_by(params[:id])
#post.liked_by #user1
#post.downvote_from #user2
Making sure to keep your plural and singular names distinct will help you keep in tune with Rails conventions and will help you easily identify when you're working with a collection or an individual object.

try #posts = current_user.find_up_voted_items in your controller and putting acts_as_voter in your User model.

Related

Ruby on Rails: Finding Records using passed parameters

I'm making an online magazine style website and am having difficulties getting the syntax right for my final part of the project. The relationships are working as they should I am just having trouble calling the intended records.
Each post belongs to a category with category_id being the foreign key. When a user clicks this link, <%= link_to 'News', categories_path(:category_id => 1) %>, I'd like for them to be brought to an index page showing only posts with a category_id matching the parameter in the URL.
I've been messing around in the categories_controller.rb for almost two hours now with no luck. Anyone be so kind as to throw this noob a bone?
There are a few components of what you're trying to do. We'll start with the routing side, and make our way to the controller.
First, you need to make the proper routes. Since the post belongs to a category, you will need to have the category id in order to handle performing any sort of operations on the posts. So we'd need a route like /category/:category_id/posts/:id. Luckily, Rails has something to handle this. If you nest a resources within a resources, it'll generate these routes. So, we end up with this:
resources :categories do
resources :posts
end
And that will get you what you want in terms of routes. But now we have to actually implement it. So, we're going to need to take a look at the controllers. If you notice, all of those routes have a :category_id - so looking up the category shouldn't be too difficult:
class PostsController < ApplicationController
before_action :load_category
private
def load_category
#category = Category.find(params[:category_id])
end
end
Now, you have the category loaded, and it shouldn't be too difficult to implement the other methods from there:
class PostsController < ApplicationController
before_action :load_category
def index
#posts = #category.posts
end
def show
#post = #category.posts.find(id: params[:id])
end
# ...
end
In order to reference the Post index path, you'll have to use category_posts_path helper.
Your problem is that you're trying to use an existing route to handle some new functionality (for which it was incidentally not designed). That categories_path route is meant to take you to your category index.
You need to create a method in your controller to perform the functionality you want to see.
class PostsController < ApplicationController
...
def posts_by_category
#posts_by_category = Post.where("category_id = ?", params[:category_id])
end
...
end
Then you're going to need a view to display your #posts_by_category array (I'll leave this exercise to you).
And now for the key to your problem: you need a route pointing to the posts_by_category method.
get 'posts/posts_by_category' => 'posts#posts_by_category'
Now you should be able to create your link with the correct route:
<%= link_to 'News', posts_by_category_path(:category_id => 1) %>

Having multiple instance variables in rails controller action? (Rails best practices)

Say for example I have two models, posts and category. Now say I want to make it so the from the category show page you can create a new post using the form_for method. To do this, you will obviously need access to the #category variable and a new instance of a post (#post). Is this acceptable code in the controller?
#app/controllers/categories_controller.rb
def show
#category = Category.find(params[:id])
#post = Post.new
end
Or is it bad practice to have two instance variables defined in the one controller action - and if it is, what would be the best practice for a case like this?
I usually do something like:
#app/controllers/categories_controller.rb
helper_method :category
helper_method :post
def show
end
private
def category
#_category ||= params[:id] ? Category.find(params[:id]) : Category.new(params[:category])
end
def post
#_post ||= Post.new(params[:post])
end
Then, in your views, just refer to post or category (not #post or #_post). The nice thing is you can remove the same logic from your new, delete, etc methods...
Actions related to posts should be in the PostsController as much as possible.
Let's say the user is looking at all posts under the category "rails": /categories/rails
There's a button on that page to create a new post under the "rails" category, href: /posts/new?category=rails
This takes you to PostsController#new where you instantiate a new Post, validate the category param and build a view. This view could either be a new page, or a modal popping up.

Multiple forms in Rails

Not sure whether my database architecture is correct for rails. However below is my database architecture
Database Relations
Each User instance has only one PhoneBook instance.
A single Phonebook instance can have multiple Contact instances
A single Contact instance can have multiple Mobile instances
A single Contact instance can have multiple Email instances
The question is how should I implement my controller and views if I want to add a new contact for a signed in user in his phonebook.
you can do that with accepts_nested_attributes_for:, like a nested form
you could define the current user like so
controllers/application_controller.rb
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
# or find_by_authtoken!(...)
end
then you could do
controllers/phonebooks_controller.rb
def create
#phonebook = Phonebook.create(phonebook_params)
if #phonebook.save
# redirects here
end
end
.....
def phonebook_params
params.require(:phonebook).permit(:phonebook_params....).merge(:user_id => current_user)
end
and in your contacts controller
controllers/contacts_controller.rb
def create
#contact = Contact.create(contact_params)
if #contact.save
# redirects here
end
end
.....
def contact_params
params.require(:contact).permit(:contact_params....).merge(:user_id => current_user, :phonebook_id => current_user.phonebook)
end
Like that, you can use your forms in a simple manner, without having to generate routes like /user/id/phonebook/id/contacts
in addition to the links below the first answer, maybe have a look at this basic form. It it is not a direct answer to your question, but maybe it'll help you getting an idea of how a form could look like.

Using Devise, how do I make it so that a user can only edit/view items belonging to them

I've been struggling with this for a while now, so I thought I'd ask the experts.
I am trying to make it so that Users can only edit/view Items that they have created using Devise.
I have Users set up and working well. Items are being created with a user associated with them and I can verify this via rails console.
def create
#item = Item.new(params[:item])
#item.user = current_user
end
What I am trying to do now is to make it so that once logged in, users can only see the items that they have created, and no others.
In my Items controller have tried replacing:
def index
#items = Items.all
end
with
def index
#items = current_user.Items.find(params[:id])
end
but this doens't seem to work for me and I get
undefined method `Items' for #<User:0x007fdf3ea847e0>
Can anyone offer any advice as to what to try next?
Thanks so much.
Maybe I`m old school but I would not use current_user to find records, only to verify permissions. I would use the primary key relationships directly (they don't change):
#items = Item.find(:all, :conditions => { :user_id => current_user[:id] }
or
#items = Item.find_all_by_user_id current_user[:id]
As for setting permissions, devise actually doesn`t let you do that BUT there is the excellent supplement called Cancan, you should definitely look into it. With Cancan, you will have an ability.rb class that will define your permissions. What you are looking for then becomes:
class Ability
can [:read, :show, :edit, :update, :delete, :destroy], Item do |item|
item.user_id == user.id
end
# or
can :manage, Item do |item|
item.user_id == user.id
end
end
reading the Cancan docs would clarify the code above.
What you're trying to do is really close…
current_user is an "instance" of the User class.
What you want to do is use the association from the user instance, which is a special method applied to every user—"items". (If the Item class also has a belongs_to :user it'll have a method called user as well)
You want current_user.items.find(params[:id])
Also, when you create it, you could also use current_user.items.create(params[:item])
If I'm understanding your question, I think you might want to check out an authorization library - like CanCan to do this.
https://github.com/ryanb/cancan
It works pretty slick to handle permission type things like this. Many people use this library in conjunction with Devise.

Where do I put 'helper' methods?

In my Ruby on Rails app, I've got:
class AdminController < ApplicationController
def create
if request.post? and params[:role_data]
parse_role_data(params[:role_data])
end
end
end
and also
module AdminHelper
def parse_role_data(roledata)
...
end
end
Yet I get an error saying parse_role_data is not defined. What am I doing wrong?
Helpers are mostly used for complex output-related tasks, like making a HTML table for calendar out of a list of dates. Anything related to the business rules like parsing a file should go in the associated model, a possible example below:
class Admin < ActiveRecord::Base
def self.parse_role_data(roledata)
...
end
end
#Call in your controller like this
Admin.parse_role_data(roledata)
Also look into using (RESTful routes or the :conditions option)[http://api.rubyonrails.org/classes/ActionController/Routing.html] when making routes, instead of checking for request.post? in your controller.
Shouldn't you be accessing the parse_role_data through the AdminHelper?
Update 1: check this
http://www.johnyerhot.com/2008/01/10/rails-using-helpers-in-you-controller/
From the looks of if you're trying to create a UI for adding roles to users. I'm going to assume you have a UsersController already, so I would suggest adding a Role model and a RolesController. In your routes.rb you'd do something like:
map.resources :users do |u|
u.resources :roles
end
This will allow you to have a route like:
/users/3/roles
In your RolesController you'd do something like:
def create
#user = User.find_by_username(params[:user_id])
#role = #user.roles.build(params[:role])
if #role.valid?
#role.save!
redirect_to #user
else
render :action => 'new'
end
end
This will take the role params data from the form displayed in the new action and create a new role model for this user. Hopefully this is a good starting point for you.

Resources