Rails - How to route for users with permalink? - ruby-on-rails

For my rails application, I am trying to create a random permalink for my users so that it is not localhost:3000/users/:id, but rather, it is localhost:3000/users/permalink.
I have followed the post made here:
how to make ID a random 8 digit alphanumeric in rails?
Following the post, I have been able to create the random permalink column and pages for my users, but have not been able to get the sub-pages to work. For my users, I currently have sub-pages: Followers, etc.
Question: The pages are currently routed to localhost:3000/users/:id/followers, etc. But does somebody know how to fix routes.rb so that I can also route these pages to localhost:3000/users/permalink/followers, etc.
routes.rb
match 'users/:permalink' => 'users#show', :as => "show_user"
resources :users do
member do
get :followers
end
end
user.rb
attr_accessible :permalink
before_create :make_it_permalink
def make_it_permalink
self.permalink = SecureRandom.base64(8)
end
users_controller.rb
def show
#user = User.find_by_permalink(params[:permalink])
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.page(params[:page]).per_page(5)
render 'show_follow'
end
users/_header.html.erb
<%= render 'users/followerstats' %>
users/_followerstats.html.erb
<a href = "<%= followers_user_path(#user) %>">
My Followers ( <%= #user.followers.count %> )
</a>
users/show_follow.html.erb
<div class = "container">
<%= render 'header' %>
<% provide(:title, #title) %>
<div class="row">
<div class="span12">
<h4><%= #title %></h4>
<% if #users.any? %>
<ul class="users">
<%= render #users %>
</ul>
<%= will_paginate %>
<% end %>
/div>
</div>

I got it to work by adding the following:
routes.rb
match 'users/:permalink/followers' => 'users#followers', :as => "followers_user"
users_controller.rb
def followers
#title = "Followers"
#user = User.find_by_permalink(params[:permalink])
#users = #user.followers.page(params[:page]).per_page(5)
render 'show_follow'
end
users/_followerstats.html.erb
<a href = "<%= followers_user_path(#user.permalink) %>">
My Followers ( <%= #user.followers.count %> )
</a>
*****UPDATE:**
Based on suggestion of #Benjamin Sinclaire and post here (Best way to create unique token in Rails?), I fixed up the make_it_permalink in user.rb:
def make_it_permalink
loop do
# this can create permalink with random 8 digit alphanumeric
self.permalink = SecureRandom.urlsafe_base64(8)
break self.permalink unless User.where(permalink: self.permalink).exists?
end
end

Ok you found it while I was writing :)
Also be careful with your make_it_permalink function. There is a tiny chance that 2 users get the same permalink with your code. I suggest you to change it to:
def make_it_permalink
begin
self.permalink = SecureRandom.base64(8)
end while User.exists?(:permalink => self.permalink)
end

Related

How do I connect routes to partials?

I am working on a rails app, where one of the tabs (the user show view) has a partial that is an inner navbar of sorts. There are three links (match, activity, message), each of which correspond to a different partial that is rendered to the right of the inner navbar.
Each of the partials has a corresponding route in my routes.rb file, but they are all rendered within one controller action (user#show). As a result, my inner navbar links alone are bringing the page to the proper route, but aren't successfully rendering the partials. To resolve this, I am checking within the view for what the end of the route is, and rendering the appropriate partial accordingly.
I imagine that there is a more ideal way to accomplish what I am looking to do, but I've had trouble figuring out what that might be.
I know that I could use separate view files instead of partials to resolve this, but for the sake of modularity, I would rather not move in that direction. Is there a better way for me to link routes to the appropriate partials?
routes.rb
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
resources :messages
resources :replies
resources :posts
resources :organizations
resources :users
resources :sessions
resources :matches
root "welcome#index"
get "/users/:id/match", to: "users#show", as: "user_match"
get "/users/:id/activity", to: "users#show", as: "user_activity"
get "/users/:id/message", to: "users#show", as: "user_message"
end
users_controller
class UsersController < ApplicationController
skip_before_action :verify_authenticity_token, only: [:create]
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:notice] = "Your account was created!"
redirect_to #user
else
flash[:alert] = #user.errors.full_messages.join(", ")
render 'new'
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, :password_confirmation)
end
end
/users/show.html.erb
<div class="row">
<% if #user.id == current_user.id %>
<div class="col-md-2 justify-content-start">
<div>
<ul>
<li>
<%= link_to(user_message_path(current_user.id)) do %>
<%= fa_icon "envelope", text: "Messages" %>
<% end %>
</li>
<li>
<%= link_to(user_activity_path(current_user.id)) do %>
<%= fa_icon "comments", text: "Activity" %>
<% end %>
</li>
<li>
<%= link_to(user_match_path(current_user.id)) do %>
<%= fa_icon "handshake-o", text: "Matches" %>
<% end %>
</li>
</ul>
</div>
</div>
<% end %>
<div class="col-md-10 justify-content-center">
<% last = request.path.split('/').last%>
<% if #user.id == current_user.id %>
<% if last == "match" %>
<%= render partial: "match" %>
<% elsif last == "activity" %>
<%= render partial: "activity" %>
<% elsif last == "message" %>
<%= render partial: "message" %>
<% else %>
<%= render partial: "home"%>
<% end %>
<% else %>
<%= render partial: "home" %>
<% end %>
</div>
</div>
You have a few alternative approaches you could take, like setting each sub-page up as a new controller (UserMatchesController, etc.) and routing to them directly, or using AJAX to load the sub-sections dynamically, but your approach isn't bad. What I would do to improve on it is to make the sub-page a named segment in the route with a constraint to lock it down:
get "/users/:id/:section", to: "users#show", constraints: { section: /^(message|home|match|activity)$/ }
You can link to each section by passing that parameter as an argument:
link_to user_path(current_user.id, section: "message")
And then in your template, something like:
render partial: params[:section]

Rails Error: ActiveRecord::RecordNotFound

I am working on my first Rails project and I am running into a persistent issue. I suspect it has something to do with the routing, however, I can't seem to find anything about it online.
I assume it a rather simple fix, so please take a look and let me know if you can help.
TL;DR
What I was trying to achieve
Account detail Cards display Name, Phone number, and a note.
A delete and edit button would allow users to delete or edit.
What is happening:
Edit and Delete buttons return a weird param.
see image
Image of error, Showing Rails getting a different ID
Controller
class AccountdetailsController < ApplicationController
def index
#accountdetail = Accountdetail.all
end
#I can't find the ID to show the relevent card.
def show
#accountdetail = Accountdetail.find(params[:id])
if #accountdetail.nil?
redirect_to accountdetail_path
end
end
def new
#accountdetail = Accountdetail.new
end
def edit
#accountdetail = Accountdetail.find(params[:id])
end
def create
#accountdetail = Accountdetail.new(accountdetail_params)
if #accountdetail.save
redirect_to #accountdetail
else
render 'new'
end
end
#it affects this
def update
#accountdetail = Accountdetail.find(params[:id])
if #accountdetail.update(accountdetail_params)
redirect_to accountdetail
else
render 'edit'
end
end
#and this
def destroy
#accountdetail = Accountdetail.find(params[:id])
#accountdetail.destroy
redirect_to accountdetail_path
end
private def accountdetail_params
params.require(:accountdetail).permit(:first_name, :last_name, :phone, :notes, :id)
end
end
Index.HTML.ERB
<div class="ui card">
<div class="content">
<a class="header"><%= account.first_name %> <%= account.last_name %> </a>
<div class="meta">
<span class="date"><%= account.phone %></span>
<strong><p><%= account.notes %></p></strong> <br>
<%= link_to "edit", edit_accountdetail_path(#accountdetail) %>
<%= link_to 'Inspect', accountdetail_path(#accountdetail) %>
</div>
</div>
</div>
<% end %>
Routes
Rails.application.routes.draw do
get 'welcome/index'
resources :articles do
resources :comments
end
resources :accountdetails
root 'welcome#index'
end
In you index.html.erb replace following
<%= link_to "edit", edit_accountdetail_path(#accountdetail) %>
<%= link_to 'Inspect', accountdetail_path(#accountdetail) %>
with
<%= link_to "edit", edit_accountdetail_path(account) %>
<%= link_to 'Inspect', accountdetail_path(account) %>
#accountdetail was providing you all the records of account, as it was firing select query in controller. But here we need only one instance, so account.
Hope this helps.

Acts_as_follower display follows RoR

I am using a ruby gem called acts_as_follower
My code:
user.rb
class User < ApplicationRecord
acts_as_followable
acts_as_follower
end
users_controller.rb
def follow
#user = User.find(params[:id])
current_user.follow(#user)
redirect_to root_path
end
def unfollow
#user = User.find(params[:id])
current_user.stop_following(#user)
redirect_to root_path
end
followers.html.erb
<% #user.followers.each do |user| %>
<div class="panel panel-default col-md-offset-3 col-md-6">
<br>
<div class="panel panel-heading">
<%= avatar_for(user, size: 50) %>
<h1> <%=link_to user.name, user %></h1>
</div>
</div>
routes.rb
#followers
resources :users do
member do
get :follow
get :unfollow
end
end
How to display the users I follow?
How to display the users I follow?
I believe the method you want is all_following
user.all_following
Returns an array of every followed object for the user, this can be a
collection of different object types, eg: User, Book
So, the below should work
#user.all_following

undefined method `to_key' for #<User::ActiveRecord_Relation:0x007f1bf8c2fd60>

I am really new at rails so please forgive me if i can't see it.
I am trying to edit the permissions on each individual user and i am getting this error in form_for:
undefined method `to_key' for #<User::ActiveRecord_Relation:0x007f1bf8c2fd60>
From what i have seen on other questions on stackoverflow, this usually happens when you try to call a collection in a form but this is not the case?
view:
<div class="n-container">
<tr-form data-riot riot-tag="tr-form">
<%= form_for(#user) do |f| %>
<section>
<div class="w-container">
<h1>User permissions</h1>
<% #permissions.each do |permission| %>
<%= check_box_tag 'permission_ids[]', permission.id %>
<div>
<%= permission.description %>
</div>
<% end %>
</div>
</section>
<section>
<div class="f-controls align-right">
<%= f.submit class: "btn fill blue", value: "Submit" %>
</div>
</section>
<% end %>
</tr-form>
</div>
users_controller:
class UsersController < ApplicationController
def user_params
params.require('user').permit(
permissions: []
)
end
def permissions
#user = User.where(id: params[:user_id])
#permissions = Permission.all
end
end
routes:
resources :users do
get 'permissions'
post 'permissions'
end
Thank you in advance!
One should use ActiveRecord::FinderMethods#find, not where, to receive a single instance:
def permissions
# WRONG: where returns a relation
# #user = User.where(id: params[:user_id])
#user = User.find(params[:user_id])
#permissions = Permission.all
end
It worth to mention, that find raise a RecordNotFound exception on fail, hence you probably want to rescue from it:
def permissions
#user = User.find(params[:user_id])
#permissions = Permission.all
rescue RecordNotFound => rnf
# Log or just skip
end
where might be [ab]used here as well, but in such a case you need to explicitly say that you need the only user:
#user = User.where(id: params[:user_id]).first

'nil' is not an ActiveModel-compatible object that returns a valid partial path

First question, please be gentle :)
I am having trouble creating an index view for a client model that belongs_to the user model with a has_many association.
The error message:
'nil' is not an ActiveModel-compatible object that returns a valid partial path.
Specifically the error refers to the partial on line #11:
/views/clients/index.html
<% provide(:title, current_user.name) %>
<div class="row">
<aside class="span4">
<section>
<h1>Your clients</h1>
</section>
</aside>
<div class="span8">
<% if current_user.clients.any? %>
<ol class="clients">
<%= render #clients %>
</ol>
<%= will_paginate %>
<% end %>
</div>
</div>
/clients/_client.html.erb
<li>
<span class="client-name"><%= client.name %></span>
<span class="client-info">
Extra client info to come.
</span>
</li>
Clients controller:
class ClientsController < ApplicationController
belongs_to :user
def index
#clients = current_user.clients.paginate(page: params[:page])
end
EDIT:
Users controller if it helps...
class UsersController < ApplicationController
before_filter :authenticate_user!
def show
if current_user.admin?
#user = User.find(params[:id])
else
#user = current_user
end
end
def index
if current_user.admin?
#users = User.paginate(page: params[:page])
else
redirect_to root_path
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_path
end
end
As you can probably tell I am new to rails, but have searched to ensure this hasn't been covered already.
Should you be passing :page => params[:page] to paginate instead?
I declared belongs_to in the clients controller and the model instead of just the model. And didn't notice for two days.

Resources