Adding users to group in Rails - ruby-on-rails

I have a group and user model joined by a membership.
When I call the code
groups/show.html.erb
<%= link_to "Add to group", group_path(:group_id => #group.id, :user_id => user.id), :method => :put %>
It calls the update method in the groups_controller.rb
def update
#group = Group.find(params[:id])
#user = User.find(params[:user_id])
if !#group.users.find(#user)
#group.users << #user
end
end
But throws an error:
ActiveRecord::RecordNotFound in GroupsController#update
Couldn't find User with 'id'=1 [WHERE "memberships"."group_id" = ?]
I'm building my first rails app and don't know why this is happening. Thanks!

You have two mistakes:
You need to find #group by params[:group_id], because that is what you are sending.
#group.users.find(#user) will throw an error if #user is not already associated with #group, which is not what you want. You want to run #group.users << #user unless #group.users.include? #user to add a user if she isn't already in the group.

Your group path uses :group_id but the controller is looking for :id. Those should probably match.

Related

Destroying Friendship in Ruby on Rails

I'm currently having issues with destroying a friendship in ruby on rails. The friendships are being created normally (I tested this using rails console), but deleting the friendship is not working.
Below is my controller code:
class FriendshipsController < ApplicationController
def create
#current_friend = User.find(params[:friend_id])
#friendship = current_user.friendships.build(:friend_id => #current_friend.id)
#friendship_2 = #current_friend.friendships.build(:friend_id => current_user.id)
if #friendship.save && #friendship_2.save
flash[:notice] = "Friend added"
redirect_to current_user
else
flash[:notice] = "Can not add friend"
redirect_to current_user
end
end
def destroy
#friendship = current_user.friendships.find_by(friend_id: params[:id]).first
if #friendship.exists?
#friendship.destroy
end
#friendship_2 = Friendship.where(user_id: params[:id], friend_id: current_user.id).first
if #friendship_2.exists?
#friendship_2.destroy
end
flash[:notice] = "Friendship destroyed"
redirect_to current_user
end
end
Below is my form for deleting the friendship:
<%= form_for(current_user.friendships.find_by(friend_id: #user.id),
html: { method: :delete }) do |f| %>
<%= f.submit "Unfriend", class: "btn" %>
<% end %>
Note that I added the check for the friendship existing since I just kept getting an error stating that the friendship was nil, which is my current problem.
Thanks!!!
In your view you're looking up the friendship with the friend_id (which you're supplying #user.id to), and that Friendship is what you're sending to form_for, so your form will have the :id of the Friendship.
But then in your destroy action you use params[:id] (the friendship id) in your query as the friend_id (which you've previously demonstrated should be a User id not a Friendship id.
Your code should actually be:
#friendship = current_user.friendships.find_by(id: params[:id])
(and you shouldn't be calling .first on that at all because find_by already returns a single record)

Form failing to save one attribute

I have a form that is saving all but one attribute. :user_id (a foreign key) persists in coming out as nil. This is a form for creating a new comment, and it's located in my impressions#show file, so that may be part of the problem; but the other attributes (:name and :impression_id) seem to be saving fine.
Here's the form:
= form_for #comment do |f|
= f.label 'Add Comment'
%br
= f.text_area :body, placeholder: 'Comment', cols: 50, rows: 3
%br
= f.hidden_field :impression_id, :value => #impression.id
= f.hidden_field :user_id, :value => current_user.id
%br
= f.submit "Save"
The relevant section of the impressions_controller is:
def show
#impression = Impression.find(params[:id])
#play = Play.find_by(id: #impression.production.play_id)
#production = Production.find_by(id: #impression.production_id)
#comments = Comment.where(impression_id: #impression.id)
#comment = Comment.new
end
In the comments_controller, I have the following:
def new
#impression = Impression.find(params[:impression_id])
#comment = Comment.new(comment_params)
authorize #comment
end
def create
#comment = Comment.new(comment_params)
authorize #comment
if #comment.save
flash[:notice] = 'You have successfully added a comment.'
redirect_to :back
else
flash[:notice] = 'Please try again.'
render :new
end
end
private
def comment_params
params.require(:comment).permit(:body, :impression_id, :user_id)
end
As you can see, I'm trying to assign :user_id the value of current_user.id, but it isn't taking. Can anyone see what I'm missing?
I've tried adding comment_params to my impressions controller, but I get an error message that the param comment is empty . . .
Thanks!
First check current_user if the value of current_user is coming fine then it has to save user_id in your comments table.
If your user is not logged in, current_user is nil.
Make sure that your user is logged in
To answer your question...
Maybe your hidden field is not sending user_id inside the comments subhash. Check your params and see. Your comment_params method expects it to be inside the comments subhash.
And how do you check these params?
Either look at your rails console after each form submission, or
if you are using rails 4.2, check https://gorails.com/episodes/rails-4-2-introduction about 4 minutes in...,
or...
in your gemfile, put
group :development do
gem 'better_errors'
gem 'binding_of_caller'
end
bundle (in your terminal), restart your rails server
and then in your controller code, at a line where current_user SHOULD exist, type the line raise "oops", run the code, get an error, then go to http://localhost:3000/__better_errors to see your console
This will give you an error page with a console, in which you can type out any variable name (params, current_user, whatever) and check its value.

How do I create friends?

I can't seem to get Amistad friendships to work correctly. I am getting the following error:
ActiveRecord::RecordNotFound in FriendshipsController#update
Couldn't find Friendship with id=29
I am also using devise and cancan. I followed the gem setup on the wiki pages and created my controller as described in this related post.
class FriendshipsController < ApplicationController
before_filter :authenticate_user!
def index
#friends = current_user.friends
#pending_invited_by = current_user.pending_invited_by
#pending_invited = current_user.pending_invited
end
def create
#friend = User.find(params[:user_id])
#friendship_created = current_user.invite(#friend)
if #friendship_created
redirect_to users_path, :notice => "Your friend request is pending"
end
end
def update
#friend = User.find(params[:user_id])
#friends = current_user.friends
#pending_invited_by = current_user.pending_invited_by
redirect_to users_path, :notice => "You are now friends!"
end
def destroy
#friend = User.find(params[:user_id])
#friendship = current_user.send(:find_any_friendship_with, #friend)
if #friendship
#friendship.delete
#removed = true
redirect_to users_path, :notice => "You are no longer friends!"
end
end
def createblock
#friend = User.find(params[:user_id])
current_user.block #friend
redirect_to users_path, :notice => "You have blocked #{#friend.first_name}"
end
end
I loop though my users in the following manner checking the current status of the user and offering appropriate actions.
<% if current_user.friend_with? user %>
<%= link_to "Unfriend", friend_path(user), :method => "delete", :class => 'btn btn-mini' %>
<% elsif current_user.invited? user %>
<span class="btn btn-mini disabled">Pending</span>
<% elsif user.invited? current_user %>
<%= link_to "Accept", friend_path(user), :method => "put", :class => 'request-approve btn btn-mini' %>
<%= link_to "Decline", friend_path(user), :method => "delete", :class => 'request-decline btn btn-mini' %>
<% else %>
<%= link_to "Add friend", friends_path(:user_id => user), :method => "post", :class => 'btn btn-mini' %>
<% end %>
Figured it would be useful to see what the friendships table looks like in my schema:
create_table "friendships", :force => true do |t|
t.integer "friendable_id"
t.integer "friend_id"
t.integer "blocker_id"
t.boolean "pending", :default => true
end
add_index "friendships", ["friendable_id", "friend_id"], :name => "index_friendships_on_friendable_id_and_friend_id", :unique => true
I understand the error just cannot figure out how this should change. I think my issue is that I am passing in a friend id and it is expecting a friendship id. My only problem with this solution is that every example or post I can find suggests passing user_id, like this post above where the answerer states the gem developer supplied the code he answers with.
What I feel like I need in my update method is to replace:
#friend = User.find(params[:id])
With this:
#friendship = Friendship.find_by_friend_id(params[:id])
EDIT
I can successfully request a friend, I just cannot accept or decline a friend. I a listing of users, clicking the "Add Friend" link creates the record in the friendships db correctly. If I log ins as that recently requested user and attempt to accept the request is when I get the above error. This also occurs if I attempt to decline the request.
The friends method you asked to see come with the amistad gem, here is the code for that method. As for my Ruby logs the section that displays the error was very long, so I have included it in this gist.
Given my current reputation, I can only post an answer instead of a comment to your question but as far as I can see from the controller sources you posted, you are not calling current_user.approve #friend in your update action.
I used this gem in one of my projects recently without running into any problems. The controller actions look like this:
def update
#friend = User.find_by_id(params[:id])
if current_user.approve #friend
redirect_to friendships_path, notice: t('.confirmation_successful')
else
redirect_to friendships_path, alert: t('.confirmation_unsuccessful')
end
end
def destroy
#friend = User.find_by_id(params[:id])
if current_user.remove_friendship #friend
redirect_to friendships_path, notice: t('.deletion_successful')
else
redirect_to friendships_path, alert: t('.deletion_unsuccessful')
end
end
I hope this helps.
The lookup problem is because you're passing ids inconsistently. In 3 of the links, you're passing the User object directly, which should automatically store the id in params[:id], which you can use in your action as User.find(params[:id]). But in your actions, you're extracting it from params[:user_id], which is empty. Not sure how you're getting an ID of 29 in your error message (or 32 or whatever), but...
If you change all your actions to expect params[:id] and switch the "Add friend" path link to pass in a User object the way the others already are, you should be passing the right data in the right parameter, and the lookup should straighten itself out.
Of course, as Wonky Business points out, you're not actually calling approve in your update method, so nothing will actually link, but at least you should be finding all your model objects.
As an aside, it appears from your paths you're remapping the friendship named routes to friend instead. That's muddling the issue because none of the RESTful routes are actually doing what their noun/verb combination implies: if you call friends_path(user) with a POST, there should be a new Friend object when you're done, but this controller is creating and destroying Friendship objects and leaving the Friend objects alone.
If you delete that alias and switch to friendship_path and so forth, then REST will actually be doing what it says it is: managing friendship objects.
Hope that helps!

Attributes sent from link_to to create action is not saved

I have the following link_to in my index.html.erb. Whenever I click on the link, it will create a new record in retrieval_requests table but for some reason user_id and package_id was not saved.
I'm not sure on how to go about this. I would appreciate any help.
Thanks.
index.html.erb
.
.
<%= link_to "Retrieve this package", retrieval_requests_path(user_id: current_user.id, package_id: item.package.id), :method => :post %>
.
.
retrieval_requests_controller.rb
class RetrievalRequestsController < ApplicationController
def index
#items = Item.where(:user_id => current_user.id)
end
def create
#retrieval_request = RetrievalRequest.new(params[:retrieval_request])
if #retrieval_request.save
redirect_to retrieval_requests_path, notice: "Successfully created retrieval request."
else
render :new
end
end
end
Since you're using a link and not a form, you're going to have to create the record with the individual params.
So instead of using params[:retrieval_request] (which don't exist), use params[:user_id] and params[:package_id].
When you pass parameters to a link helper, such as:
retrieval_requests_path(user_id: 3, package_id: 26)
It will create a URL that looks something like the following, depending on how you set up your routes:
"/retrieval_requests/3/26"
# or with no user_id or package_id set in routes:
"/retrieval_requests?user_id=3&package_id=26"
Then in your controller you need to grab those params separately.

Best way to check user's permission by id?

In Rails3 application i have a number of models with user_id - this way i'm saying: it was created by some user.
Like:
current_user.id #=> 1
#item.user_id #=> 1
# this item created by user with id 1
And i want to restrict current_user's acess to items which was not created by him/her.
Something like:
if #item.user_id == current_user.id
#everything is fine
else
#redirect somwhere with flash "You don't have an access here"
end
What is the best way for this, because i have multiple number of models (and controllers to show/edit/destroy) with such a user_id?
Use CanCan!
With it you will be able to define permissions declaratively, like this:
can :read, Project, :user_id => user.id
And later enforce this rule:
def show
#project = Project.find(params[:id])
authorize! :read, #project
end
authorize! will raise an exception, but you can check in a more peaceful manner:
<%= link_to 'Link to a project', #project if can? :read, #project %>
You can intercept authorization errors and handle them in one place:
class ApplicationController < ActionController::Base
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_url, :alert => exception.message
end
end
The simplest way to do this, is to use Active Record's has_many.
Namely, in a controller, whenever you load the Item, you just say
#item = current_user.items.find(params[:id])
This way you don't have to do any work to check.

Resources