I am trying to finish my forum application. I want arrange user Friendships. I am using devise as authentication system. I want some suggestions.
Shall i nest the resources in User and Friendships. This is the way Railscasts used:
devise_for :users
resources :users, only: [:index, :show]
resources :friendships, only: [:create, :destroy]
This is how i used:
devise_for :users
resources :users, only: [:index, :show] do
resources :friendships, only: [:create, :destroy]
end
My real problem is that . I want to use friendships in a way that , signed_in user can check the users list , and add a friend if he is not in the friendslist. Now i can add a friend multiple times. And also ,user can add himself as a friend.
How can i fix this links with if/else statements:
showing a user profile works:
<section>
<h1>
<%= #user.username %>
</h1>
<%= link_to "Arkadaşlarıma Ekle", friendships_path(:friend_id => #user), :method => :post,class: "btn btn-large btn-primary" %>
</section>
and herei I cant find the way to show the friend's profile. :
<% for friendship in #friendships %>
<%= link_to friendship.friend.username, '#' %>
(<%= link_to 'Sil', friendship, :method => :delete %>)
<% end %>
..
rake routes:
I can understand that , I have to use correct if/elses but i am lost in nesting my resources and routing. . Thanks for explanations..
These are my edits:
In my users_controller:
def user_is_friend?(other_user)
current_user.friendships.find_by_friend_id(other_user.id)
end
<% unless user_is_friend?(#user) %>
<%= button_to "Add friend", friendships_path(:friend_id => user), :method => :post %>
<% end %>
Is it correct?
I would recommend you, in your user index view, when you are looping with a variable user (for #users do |user|) to add first a condition:
<% unless current_user == user %>
--> this will allow you to process all users except the current_user (i.e. yourself)
Then, you can define this:
<% user_is_friend = current_user.friendships.find_by_friend_id(user.id) %>
This will be TRUE if the user is already in your friend's list
And then, a last piece:
<% if !user_is_friend %>
<%= button_to "Add friend", friendships_path(:friend_id => user), :method => :post %>
<% end %>
Related
I have the following code:
<%= link_to new_book_path(controller: :books, action: 'new', id: comment) %>
#also tried:
<%= link_to new_book_path(comment.user.id) %>
#outputs: undefined id
<%= link_to new_book_path(comment.user_id) %>
#leads to my (logged-in user) book list, not this user's
<%= link_to new_book_path(comment.user) %>
#same
<%= link_to new_book_path(comment) do %>
#same. comment.post.book.user.id also same.
I was wondering how I can get to this particular user's book list through link_to from this user's comment. I keep going to my own.
My routes are:
resources :books do
resources :posts, shallow: true
end
resources :posts do
resources :comments, shallow: true
end
resources :users do
resources :comments, shallow: true
end
1) in comment:
<%= link_to comment.user.name, books_list_path(user_id: comment.user.id)
books_list_path is path where books are listed
2) in action where books are listed (index or new action):
class BooksController < ApplicationController
..
def index
#books = Book.where(user: params[:user_id])
end
..
end
3) in books/index.html.erb
<% #books.each do |book| %>
<%= book.name %><br>
<% end %>
So I'm getting the error stated in the title. What code would I have to write in my Postscontroller to fix this? I'm not sure what I would have to do here, would I have to define rsvp in my post controller? If thats the case how would I go about doing that?
class RsvpController < ApplicationController
def create
rsvp = current_user.rsvps.build({post_id: params[:id]})
if rsvp.save
end
end
end
Show.html.erb
<p>
<strong>Date:</strong>
<%= #post.date %>
</p>
<p>where:<%=#post.longitude %>, <%=#post.latitude%></p>
<p>
<strong>Name:</strong>
<%= #post.name %>
</p>
<p>
<strong>User_id:</strong>
<%= #post.user_id %>
</p>
<p>
<strong>Description:</strong>
<%= #post.description %>
</p>
<% if current_user == #post.user %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<%end%>
<%= link_to 'Back', posts_path %>
<%= button_to "Rsvp now", rsvp_post_path(#post), class: "btn btn-primary" %>
routes.rb
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
resources :posts
devise_for :users
root 'home#index'
get 'home/ruby_meetup'
resources :posts do
post 'rsvp', on: :member
end
Also I want it so it shows the list of people who already rsvped for the event. How would I go about doing that? I would appreciate some help as I'm still learning rails and this is my first project.
By the following line, you define the route:
resources :posts do
post 'rsvp', on: :member
end
By this line you're requesting this route:
To process the request, you need a controller action rsvp, which actually the error states.
Thus, just define the action (method) and process the request:
class RsvpController < ApplicationController
def create
rsvp = current_user.rsvps.build({post_id: params[:id]})
if rsvp.save
end
end
def rsvp
# process the request here...
end
end
UPDATE - I'VE NOW SOLVED THIS PROBLEM - I created a partial for the each Course item and rendered them from the main listing view. Thanks for all your help, I'm not really sure why it worked but it did END OF UPDATE
Apologies if this looks like a repeat posting but I've tried applying solutions to similar questions and they haven't worked, I'm stuck! Any suggestions welcomed, thank you.
Problem
I have a 'Courses' model which belongs to a 'Listings' model. The courses are created and deleted on a page belonging to Listing i.e. "/listing/2/courses"
Error Message
No route matches [DELETE] "/listings/2/courses"
Courses Controller def destroy detail
class CoursesController < ApplicationController
before_action :authenticate_user!, except: [:show]
before_action :set_listing
before_action :set_course, except: [:index, :new, :create]
def destroy
#course = #listing.courses.find(params[:id])
#course.destroy
flash[:notice] = "Course deleted!"
redirect_back(fallback_location: request.referer)
end
private
def set_course
#listing = Listing.find(params[:listing_id])
#course = Course.find(params[:id])
end
def set_listing
#listing = Listing.find(params[:listing_id])
end
def course_params
params.require(:course).permit(:name, :curriculum_type, :summary, :address, :course_places, :start_date, :finish_date, :price)
end
end
listing/listingID/courses page detail
<%= #listing.courses.each do |f| %>
<div class="jumbotron">
<ul>
<li>Name = <%= f.name %></li>
<li>Type of course = <%= f.curriculum_type %></li>
<li>Number of places = <%= f.course_places %></li>
<li>Start Date = <%= f.start_date %></li>
<li>Finish Date = <%= f.finish_date %></li>
<li>Price (£) = <%= f.price %></li>
<%= link_to "Delete Course", listing_courses_path(#listing, #course), method: :delete %>
</ul>
</div>
<% end %>
Routes.rb detail
resources :users, only: [:show]
resources :listings, except: [:edit] do
member do
get 'listing'
get 'pricing'
get 'description'
get 'photo_upload'
get 'amenities'
get 'location'
get 'courses'
end
resources :courses, except: [:edit] do
member do
get 'listing'
get 'pricing'
get 'description'
get 'photo_upload'
get 'amenities'
get 'location'
end
end
end
<%= link_to listing_course_path(#listing,f), :method => :delete, :data => { :confirm => 'Are you sure?' } %>
or try
<%= link_to listing_course_path(#listing,id: f.try(:id)), :method => :delete, :data => { :confirm => 'Are you sure?' } %>
route.rb
resources :listings do
resources :courses
end
By default, destroy method expects an ID as it's a member route. You are using listing/listingID/courses route without an ID. For that you need to define listing and/or courses as singular resources (read this) like:
resource :courses do
:
end
as described in this answer or make destroy a collection route like so:
resources :courses, except: [:edit] do
delete :destroy, on: :collection
:
rest...
end
Try and see if this works.
By the way, this looks a bit redundant as you are iterating over each #listing.courses and calling courses#destroy where you are destroying all the courses of #listing anyhow. So, why do #listing.courses.each in the first place. You should have either used a listing#destroy_courses method or remove #listing.courses.each iteration.
Update path in link_to to listing_course_path(#listing, #course) from listing_courses_path(#listing, #course)
<%= link_to "Delete Course", listing_course_path(#listing, f), method: :delete %>
I'm building my second-ever basic Ruby on Rails application and having fun doing it, but have gotten stuck at precisely the same place that gave me trouble (and was never solved) on my last effort: the PUT or PATCH request.
My application has two models: entries and users. A logged-in user should be able to edit only those entries that were originally created by that user.
CONTROLLER
class EntriesController < ApplicationController
# authenticate user (Devise)
before_action :authenticate_user!, :except => [:index, :show]
# set entry upon page load
before_action :set_entry, :only => [:show, :edit, :update, :destroy]
# GET request - display all entries
def index
#all_entries = Entry.all
end
# GET request - display an individual entry
def show
# nothing required here because entry identified with before_action :set_entry on line 2 above
end
# GET request - access form to create a new entry
def new
#entry = Entry.new
#user = User.find(current_user[:id])
end
# GET request - access form to update an existing entry
def edit
if #entry[:user_id] != current_user[:id]
redirect_to root_path
else
redirect_to edit_entry_path
end
end
# POST request - make a new entry/save new data into db
def create
user = current_user[:id]
Entry.create({
entry_title: params[:entry][:entry_title],
book_title: params[:entry][:book_title],
text: params[:entry][:text],
img_url: params[:entry][:img_url],
tag: params[:entry][:tag],
created_at: params[:entry][:created_at],
user_id: user
})
redirect_to entries_path
end
# PUT request - save changes to an existing entry
def update
if #entry.update(entry_params)
redirect_to entry_path
else
render :new
end
end
# DELETE request - delete an existing entry from db
def destroy
#entry.destroy
redirect_to entries_path
end
private
def set_entry
#entry = Entry.find(params[:id])
end
def entry_params
params.require(:entry).permit(:email, :text, :tag)
end
end
VIEW (show.html.erb - shows a single entry and includes links allowing the logged-in user who originally authored the entry to edit or delete it)
<h3>Selected Entry</h3>
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-6">
<div>Entry title: <%= #entry.entry_title %></div>
<div>Book title: <%= #entry.book_title %></div>
<div>Text: <%= #entry.text %></div>
</div>
<div class="col-md-4">
<div><%= #entry.created_at.strftime("%b %d, %Y") %></div>
<div>Submitted by: <i><%= #entry.user.email %></i></div>
<div>File under: <i><%= #entry.tag %></i></div>
<% if current_user %>
<%= link_to 'Edit', #entry, :method => 'update' %>
<%= link_to 'Delete', #entry, :method => 'delete' %>
<% end %>
</div>
</div>
ROUTES.RB - At first my routes were the commented-out lines, but then I had a thought that was either madness or sudden realization - should only the GET routes lead with "get"? So that's the non-commented-out attempt you see. Somehow the app works (except for the issue at hand) both ways.
In researching I've come across routes defined using a much more elaborate syntax than that I'm using here. I've been unable to figure out whether a given way of doing things is different convention, outdated, or just inadequate to the task.
Rails.application.routes.draw do
devise_for :users
resources :entries
# root 'entries#index'
# get '/entries' => 'entries#index'
# get '/users' => 'users#index'
# get '/entries/:id' => 'entries#show'
# get '/entries/:id' => 'entries#update'
# get '/entries/new' => 'entries#new'
# get '/entries/:id/edit' => 'entries#edit'
# get '/users/:id' => 'users#show'
# get '/about' => 'pages#index'
root 'entries#index'
get '/entries' => 'entries#index'
get '/entries/new' => 'entries#new'
post '/entries' => 'entries#create'
get '/entries/:id' => 'entries#show'
get '/entries/:id/edit' => 'entries#edit'
put '/entries/:id' => 'entries#update'
delete '/entries/:id' => 'entries#destroy'
get '/users' => 'users#index'
get '/users/:id' => 'users#show'
get '/about' => 'pages#index'
end
Thanks in advance for any insight. If additional context is needed I'm happy to provide.
Edited to add:
PARTIAL (_form.html.erb)
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-6" id="form-container">
<%= form_for #entry do |form| %>
<br>
<%= form.text_field :entry_title, :size => 59, :placeholder => "Entry Title"%>
<br><br>
<%= form.text_field :book_title, :size => 59, :placeholder => "Book Title"%>
<br><br>
<%= form.text_field :img_url, :size => 59, :placeholder => "Image URL"%>
<br><br>
<%= form.text_area :text, :placeholder => "Text" %>
<br><br>
<%= form.text_field :tag, :placeholder => "Tag" %>
<br><br>
<%= form.submit %>
<% end %>
</div>
<div class="col-md-4"></div>
</div>
To edit a record you
first, should use a GET request to get the edit form
second, should submit that form using a PUT/PATCH request
To get to the edit form you should link to the edit path for your entry
<%= link_to 'Edit', edit_entry_path(#entry) %>
The Rails form helpers will automatically set the form to submit with the proper method, PUT OR PATCH.
:method in link_to helpers refers to HTML verb (get, post, etc), while controllers methods naming convention is action.
link_to
You need something as
<%= link_to 'Edit', #entry, :method => 'put' %>
or
<%= link_to 'Edit', #entry, :action => 'update' %>
At a glance you are trying to post with the edit link. Remember new/edit are get methods to render form, so just just delete method part in your links. Like from
<%= link_to 'Edit', #entry, :method => 'update' %>
to
<%= link_to 'Edit', edit_entry_path(#entry) %>
I'm building my second-ever basic Ruby on Rails application
Congrats! You need at least 3 more before it all starts to make sense
To add to the existing answers, you'll be best looking at the resources directive to clean the routes up:
#config/routes.rb
root 'entries#index'
devise_for :users
resources :entries
resources :pages, only: [:index], path_names: { index: "about" }
resources :users, only: [:index,:show]
--
A logged-in user should be able to edit only those entries that were originally created by that user.
This is known as authorization.
Authentication = is user logged in?
Authorization = can user do this?
Although people confuse Devise with being able to handle authorization, it only handles authentication. Whilst you have a simple implementation of this in your controller, you should check out either the CanCanCan or Pundit gems:
#Gemfile
gem "cancancan"
#app/models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :manage, Post, user_id: user.id
end
end
#app/controllers/entries_controller.rb
class EntriesController < ApplicationController
def edit
authorize! :edit, #entry
end
end
--
Finally, to answer your question directly, you're calling the update method (which doesn't exist) to access the edit view:
<% if current_user %>
<%= link_to 'Edit', #entry, :method => 'update' %>
<%= link_to 'Delete', #entry, :method => 'delete' %>
<% end %>
You should read up about http verbs - this is what the "method" option invokes with the link. As mentioned above, you don't need to set the method for edit as it uses GET. Update uses put/patch, which I can explain later.
A much better way to achieve what you want would be the following:
<%= link_to "Edit", edit_entry_path(#entry) if can? :edit, #entry %>
<%= link_to "Delete", #entry, method: :delete, if can? :destroy, #entry %>
The above uses the CanCanCan authorization method can?
I am having a lot of trouble trying to pass an id from a model into a different controller. I've read similar posts and tried different solutions, but can't seem to pass the Event id into the Invitations controller.
UPDATE
The goal is that a user, while looking at an event they created, will click on an Invite link. This link needs to save the ID from the Event it came from so that Invitations can be associated with that event.
In my models, I for the Event model, I have 'has_many "invitations' and for the Invitation model I have 'belongs_to :events'
Here is my code.
show.html.erb within my Events folder:
<%= link_to 'Invite guests', invitations_path(:event => #event.id) %>
This link maps to 'invitations#new':
<% provide(:title, 'Invite Guests') %>
<h1>Invite your guests</h1>
<%= form_for(#invitation) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, :placeholder => "Name" %> <br>
<%= f.label :email %>
<%= f.text_field :email, :placeholder => "Email" %> <br>
<%= f.submit "Send" %>
InvitationsController:
def index
end
def new
#invitation = Invitation.new
end
def create
user = current_user.events.find(params[:event])
#invitation = user.invitations.build(params[:invitation])
if #invitation.save
flash[:success] = "Invitations sent!"
redirect_to user
else
render '/home'
end
end
I know that if I replace:
user = current_user.events.find(params[:event])
with:
user = current_user.events.find(1)
Routes:
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :events, only: [:show, :create, :destroy]
resources :invitations, only: [:new, :create]
root to: '/home'
match '/signup', to: 'users#new'
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
match '/events', to: 'events#new'
match '/invitations', to: 'invitations#new'
Everything works fine, but of course, this is not suitable since I am hard coding the Event ID. I am pretty new to RoR (about 2 months of experience) so there may be a more efficient way to do this. So any help would be much appreciated.
<%= link_to 'Invite guests', invitations_path(:event => #event.id) %>
Goes to:
def index
Not to:
def create
If you want to go to create do:
<%= link_to 'Invite guests', invitations_path(:event => #event.id), method: :post %>