I am trying to implement Michael Hartl's follow feature into my application, so I've had to make some little changes as I've gone along.
Problem is when I click the followers/following button. I get the error
Here is my UsersController
class UsersController < ApplicationController
def show
#user = User.find_by_username(params[:id])
end
def user_params
params.require(:user).permit(:avatar)
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
If you need more information to get help me solve this problem, do not hesitate to let me know.
Since you are passing the name of the user instead of the id, you should use:
User.find_by(name: "user_name")
Related
I am trying to use paginate with act as votable but I'm getting a few errors. My favorites method is to display a page that shows only items that the user upvoted.
def favorites
#title = "Favorites"
#user = User.find(params[:id])
#favorites = #user.find_up_voted_items
render 'show_favorites'
end
This shows all upvoted via act as votable items. However, when I try to use paginate on the page by editing the # favorites with this:
#favorites = #user.find_up_voted_items.paginate(page: params[:page])
I get an undefined method for paginate.
I am already using paginate for other pages:
def index
#users = User.paginate(page: params[:page], per_page: 30)
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
What am I missing with my favorites method?
In my app, when a user logins he/she is redirected to the users profile page. Say he/she is redirected to http://localhost:3000/users/1
If he/she replaces 1 with any other number I want them to redirect to there
current profile no matter if users exits in the database or not
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
log_in user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
#current_user = nil
reset_session
redirect_to root_path
end
end
User Controller:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:new, :show, :edit, :update]
before_action :correct_user, only: [:new, :show, :edit, :update]
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.new(set_params)
if #user.save
redirect_to new_sessions_path
else
render 'new'
end
end
def show
#user = User.find(params[:id])
#posts = #user.posts
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(update_params)
redirect_to #user
else
render 'edit'
end
end
private
def set_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def update_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
end
Currenty if user type in search bar localhost:3000/users/5 and user with id 5 does not exists in database it shows error
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User with 'id'=3
but I want to simply redirect to currently logged in users profile page.
If users type in search bar localhost:3000/users/3 and user with this id exists in db , currenty it show an error that firefox is not able to process this request but i want it redirect to its default page i.e,,user's profile page.
Create another controller call it UserController and don't depend on id. Instead figure out the current user from the session and display that user. So the show method for this controller would look like this:
def show
#user = User.find(session["user_id]")
#posts = #user.posts
end
Also, you might want to protect your UsersController by validating if the current user has access to view / update the user being queried for.
Just change your UsersController#correct_user to catch ActiveRecord NotFound exception:
class UsersController < ApplicationController
...
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
rescue ActiveRecord::RecordNotFound
redirect_to(root_url)
end
end
I would use "where" and ".take" in Users show method. The find method brakes the code when it does not find anything
def show
#user = User.where("id" => params[:id]).take
if #user.present?
#posts = #user.posts
else
redirect_to(root_url)
end
end
Or you can redirect instead of root_url to a more friendly error view that shows User not found
Im trying to register users on my rails site. When I click the register button on the register page this is what shows up:
ActiveModel::ForbiddenAttributesError in UserController#register
ActiveModel::ForbiddenAttributesError
Here is the code for my user_controller.rb file:
class UserController < ApplicationController
def index
#title = "RailsSpace User Hub"
end
def register
#title = "Register"
if request.post? and params[:user]
#user = User.new(params[:user])
end
if #user.save
flash[:notice] = "User #{#user.screen_name} created!"
redirect_to :action => "index"
end
end
end
It's complaining about line 11: #user = User.new(params[:user]) Im following code from a book so I dont know what's wrong with it.
Does anyone have any suggestions? Thanks for all your help in advance.
You should use strong parameters.
The UserController should look like this:
class UserController < ApplicationController
def index
#title = "RailsSpace User Hub"
end
def register
#title = "Register"
if request.post? and params[:user]
#user = User.new(user_params)
end
if #user.save
flash[:notice] = "User #{#user.screen_name} created!"
redirect_to :action => "index"
end
end
private
def user_params
# Add the user attributes that you sent with post (form) to the permit method.
params.require(:user).permit(:name, :screen_name)
end
end
I am getting an undefined method 'errors' for nil:NilClass error when attempting to display errors in a view from validations in Rails. I followed this example Rails validation error messages displays the error messages and array, taking its answer into account.
An example of a validation I have in the user model is:
validates_length_of :dog_name, minimum: 0, maximum: 30, message: 'cannot have more than 30 characters'
View code
<% if #user.errors.any? %>
<div id="errorExplanation">
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Users Controller
class UsersController < ApplicationController
#before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
#:following, :followers]
before_filter :requireLogin
#before_action :requireDog
def requireLogin
if session[:user_id] == nil
redirect_to "/"
end
end
def requireDog
#user = User.find(session[:user_id])
if(#user.dog_name == "Empty")
redirect_to "users/new/"
end
end
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def new
#user = User.new
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def update
#user = User.find(params[:id])
if #user.update_attributes(profile_params)
redirect_to action: "show"
# Handle a successful update.
else
render 'edit'
end
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
def going
#title = "Going"
#user = User.find(params[:id])
#users = #user.going.paginate(page: params[:page])
render 'show_gather'
end
def create
#user = User.new(profile_params)
if #user.save
redirect_to '/users'
else
render 'new'
end
end
private
def profile_params
params.require(:user).permit(:dog_name, :photo, :dog_breed, :dog_gender, :dog_age)
end
end
In other examples the user model/controller did not need to be further modified. The user does indeed exist. Am I missing something in the model or controller?
Firstly, you'd be better switching to the new validates methods:
#app/models/user.rb
class User < ActiveRecord::Base
validates :dog_name, length: { in: 0..30, message: 'cannot have more than 30 characters'}
end
-
Secondly, instead of setting #title each time - why don't you just use action_name?
Each time you load an action, the controller_name and action_name variables are set by default, to be used either in your controller or views. Why not just use the action_name variable in your view?
#app/views/layouts/application.html.erb
<title><%= action_name if controller_name == "Users" %></title>
To answer your question, the error is caused because you're trying to invoke a method on a non-existent variable:
for nil:NilClass
Ruby has a funny way of posting exceptions for undeclared variables -- instead of outwardly saying the variable doesn't exist, it invokes it under the NilClass class.
As a developer, you're then expected to read the message of undeclared method and surmise that it's the variable which isn't set...
The fix for your problem is therefore to find why the variable is not set:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new profile_params
redirect_to users_path if #user.save
end
end
The above should give you the ability to save the user, else it will re-render the new action (and show the error).
I think the problem you have is that you're explicitly loading the new action on validation problems, which will prevent it appending the required data.
To create an user in the example bellow I have to load #projects and #companies as they are mandatory for creating user.
class UsersController < ApplicationController
def new
#user = User.new
# duplication here
#projects = Project.all
#companies = Company.all
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
# and here
#projects = Project.all
#companies = Company.all
render :action => "new"
end
end
end
I have to load these dependencies in both (duplication) cases, for the new and create action when the user is invalid.
I can refactor by encapsulating these dependencies in a method load_user_dependencies.
class UsersController < ApplicationController
def new
#user = User.new
load_user_dependencies
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
load_user_dependencies
render :action => "new"
end
end
private
def load_user_dependencies
#projects = Project.all
#companies = Company.all
end
end
Or by adding them as a helper_methods.
class UsersController < ApplicationController
helper_method :projects, :companies
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
render :action => "new"
end
end
def projects
#projects ||= Project.all
end
def companies
#companies ||= Company.all
end
end
I can also create a View Object
class UserView
def products
#products ||= Product.all
end
def companies
#companies ||= Company.all
end
end
class UsersController < ApplicationController
def new
#user = User.new
#user_view = UserView.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
#user_view = UserView.new
render :action => "new"
end
end
end
Other option is to use a Presenter
class UserPresenter < SimpleDelegator
def products
#products ||= Product.all
end
def companies
#companies ||= Company.all
end
end
class UsersController < ApplicationController
def new
#user = UserPresenter.new User.new
end
def create
#user = UserPresenter.new User.new(params[:user])
if #user.save
redirect_to #user
else
render :action => "new"
end
end
end
How do you guys usually deal with scenario?
It's a bit of a personal taste issue, but in our projects if it's suitable we do it in the view, and if it's complex we do it in a before_filter.
View - suitable if it's something that doesn't have any logic, no need to create a variable for this. Super useful if your form is contained in a partial as it's only done once so easily maintained.
<%= form.select :project_id, Project.all, :id, :name %>
If it's something that can change depending on any factors then a before_filter in controller:
MyController
before_filter :find_projects, :except => [:destroy, :some_method] # Will load the values but not for the destroy or some_method actions
def find_projects
#projects = Project.where(:some conditions => true)
end