What is the best approach to do this?
#user = UserFinder.find(first_name: "Dave")
if !#user
return render :text => '// No user named Dave found'
end
if #user.last_name != "Edwards"
# This is not the guy we want
#user.destroy
# Now we want to re-execute the search again forever until we find Dave Edwards
end
Keep in mind this is an incredibly simplistic example of the real code I have, so I know that I can just filter the query in the first place, but I want it this way.
I'm not sure how to do this with begin and while blocks because destroying an unwanted user is required if we run into one.
My Attempts:
begin
#user = UserFinder.find(first_name: "Dave")
return render :text => '// No user named Dave found'
end while #user.destroy if #user.last_name != "Edwards"
This doesn't work because the while block gets the result of destroy not the comparison of last_name
P.S. Just if this is impossible this way, How to reload the Rails controller action with the same params?
Try this:
begin
#user = UserFinder.find_by_first_name("Dave")
render :text => '// No user named Dave found' if #user.nil?
return if #user.nil?
end while #user.last_name != "Edwards" && #user.destroy
Does this work for you?
while (#user = UserFinder.find(first_name: "Dave")) and #user.last_name != "Edwards"
#user.destroy
end
if #user
render ...
else
render :text => '// No user named Dave found'
end
user.destroy while user = UserFinder.where(:first_name => "Dave", :last_name => "Edwards").first
Related
User has a page where he can edit his personal information(name, date of birth,...).
But the user can delete the value from the Name field, and then go to some link with the sidebar, and the information is saved as an empty string('').
Question: how do I make such a check, the user can not go to anywhere, if at least one field is empty? Or if it is empty, then fill it with the previous data?
Controller:
def editMain
unless #user = User.find_by_id(session[:user_id])
render_404
return
end
#user.update_attributes(params[:user])
if params[:user][:first_name] == ""
#user.errors.add(:first_name, "ERROR!")
end
if params[:user][:last_name] == ""
#user.errors.add(:last_name, "ERROR!")
end
if params[:user][:birtday] == ""
#user.errors.add(:birthday, "ERROR!")
end
#countries = Country.all
#cities = City.all #where(country_id: #user.city.country.id)
if #user.errors.empty?
flash[:notice] = "Succssesful!"
else
render "editMain"
end
end
Please, help me!
Use what framework gives you. You don't have to rewrite existing things.
This code =>
unless #user = User.find_by_id(session[:user_id])
render_404
return
end
Equals to =>
#user = User.find(session[:user_id])
Dont try to validate objects in controller.
This code =>
#user.update_attributes(params[:user])
if params[:user][:first_name] == ""
#user.errors.add(:first_name, "ERROR!")
end
if params[:user][:last_name] == ""
#user.errors.add(:last_name, "ERROR!")
end
if params[:user][:birtday] == ""
#user.errors.add(:birthday, "ERROR!")
end
Equals to this =>
if #user.update_attributes(params[:user])
flash[:notice] = "Succesful!"
redirect_to some_path
else
render "editMain"
end
and use activerecord validations.
models/user.rb
validates_presence_of :first_name, :last_name, :birthday
Frankly, this piece of code is quite a mess:
1) 404 is returned when you can't find a page (vs you can't find a resource (user) with some id)
2) 404 is returned automatically by rails, if page (route) isn't found
3) Model should do validation (vs controller)
4) Rendering could be done for your automatically in most cases
I would really recommend to read through Getting tarted with Rails:
http://guides.rubyonrails.org/getting_started.html
I wrote a "follow" method in UsersController
def start_following
#user = current_user
#user_to_follow = User.find(params[:id])
unless #user_to_follow == #user
#follow_link = #user.follow_link.create(:follow_you_id => #user_to_follow.id, :user_id => #user.id)
#user.save
flash[:start_following] = "You started following" + #user_to_follow.name
else
flash[:cant_follow] = "You cannot follow yourself"
end
end
Pretty simple. And In the view, I have
<%= link_to 'Follow', follow_user_path(#user) %>
In routes,
resources :users do
member do
get 'follow' => "users#start_following", :as => 'follow'
When I click on the link, it complains: Missing template users/start_following
So, how do I make it just stay on the same page after the action?
The view page that I want to stay on is Show view of the user is to be followed.
ex: users/{user_id}. Is simply redirecting not a solution? I thought adding redirect_to {somewhere} would get rid of the error, but it didn't.
I would redirect to the user in question. If you are using the standard resourceful routes then you can just do
redirect_to(#user_to_follow)
As an aside it's generally considered bad practice to have GET requests that make changes - people normally use put/patch/post/delete requests for those. You can fall afoul of browsers pre-fetching links without the user actually clicking on them.
try:
redirect_to :back, :notice => "successfully followed someone..."
Yes redirect_to solves your problem, I suspect you forgot to add it to both branches of the unless
The code would look like this:
def start_following
#user = current_user
#user_to_follow = User.find(params[:id])
unless #user_to_follow == #user
#follow_link = #user.follow_link.create(:follow_you_id => #user_to_follow.id, :user_id => #user.id)
#user.save
flash[:start_following] = "You started following" + #user_to_follow.name
else
flash[:cant_follow] = "You cannot follow yourself"
end
redirect_to #user_to_follow
end
The action bellow creates a new comment.
A user has many statuses
A status has many comments
How can optimize this action so that head 401 and return is not repeated many times.
def create
#user = User.where(id: params[:user_id]).first
if #user
if current_user.friend_with?(#user) or current_user == #user
#status = #user.statuses.where(id: params[:status_id]).first
if #status
#comment = #status.comments.build(params[:comment])
#comment.owner = current_user
if #comment.valid?
#comment.save
current_user.create_activity(:comment_status, #comment, #user)
else
head 401 and return
end
else
head 401 and return
end
else
head 401 and return
end
else
head 401 and return
end
end
Thank you.
When do you want to return 401?
when a user has not been found
when a user is not a current user or is not a friend of that user
when a status has not been found
when new comment has not been successfully created
Instead of using so many conditionals, you can use methods that raise exceptions. When you do so, you can rescue from that exceptions with the desired behavior (rendering 401).
So my suggestions for listed conditions are:
use find! instead of where and then first.
raise something, preferably custom exception (NotAFriendError)
same as 1., use find!
use create!, it's an equivalent to new and then save! which will raise ActiveRecord::RecordInvalid exception if it fails on validation.
Here's the result:
def create
begin
#user = User.find!(params[:user_id])
raise unless current_user.friend_with?(#user) || current_user == #user
#status = #user.statuses.find!(params[:status_id])
#comment = #status.comments.
create!(params[:comment].merge(:owner => current_user))
rescue ActiveRecord::RecordNotFound, ActiveRecord::RecordInvalid
head 401
end
# everything went well, no exceptions were raised
current_user.create_activity(:comment_status, #comment, #user)
end
You have a lot of excessive checking and branching in your code, so it can be simplified to this:
def create
success = false
#user = User.find(params[:user_id])
current_user_is_friend = current_user.friend_with?(#user) || current_user == #user
if #user && current_user_is_friend && #status = #user.statuses.find(params[:status_id])
#comment = #status.comments.build(params[:comment])
#comment.owner = current_user
if #comment.save
current_user.create_activity(:comment_status, #comment, #user)
success = true
end
end
render(status: 401, content: '') unless success
end
A few things I did:
Combine a lot of the if conditions, since there was no need for them to be separate.
Change where(id: ...).first to find(...) since they're the same. Note that, if the find fails, it will give a 404. This may make more sense, though (I think it does)
Don't call #comment.valid? right before #comment.save, since save returns false if the object wasn't valid.
Use || instead of or for boolean logic (they're not the same).
Use render(status: ..., content: '') instead of head ... and return.
Use a boolean variable to track the success of the method.
I would advise that you try and pull some of this logic out into models. For example, User#friend_with should probably just return true if it's passed the same User.
def create
#user = User.where(id: params[:user_id]).first
if #user
if current_user.friend_with?(#user) or current_user == #user
#status = #user.statuses.where(id: params[:status_id]).first
if #status
#comment = #status.comments.build(params[:comment])
#comment.owner = current_user
if #comment.valid?
#comment.save
current_user.create_activity(:comment_status, #comment, #user)
everythingOK = true
end
end
end
end
head 401 and return unless everythingOK
end
When i run in my page this code :
<% user = User.find(session[:userid]) %>
i get the error :
line #1 raised:
Couldn't find User without an ID
although i have in my authentification in my sessions_controller this :
def create
if user = User.authenticate(params[:username],params[:password])
session[:user_id]= user.id
session[:language_id]= user.language_id
User.find(user.id).update_attributes(:last_login => Time.now)
redirect_to root_path , :notice => (I18n.t :"session.login_success")
else
flash.now[:alert] = (I18n.t :"session.error")
render :action => 'new'
end
end
and the session should contain the userid
In your log-in you set session[:user_id], and you try to find session[:userid] (note the spurious underscore). That's why.
Your controller is looking for params session[:user_id], but in your views it is session[:userid] so it will complain that session[:user_id] is nil.
So I have a url like the following
localhost/users/:id/posts
which gives the posts of that particular user. Now this id can be either his login (which is a string) or the id (user.id) which is technically an Integer but params[:id] is always a string. So how do I implement this an action.
#user = params[:id].is_a?(String) ? User.find_by_login(params[:id]) : User.find(params[:id])
The above code miserably fails since params[:id] is always a string. Any thoughts? Thanks.
When I've done this, I've actually had two separate controller actions-- show and show_by_login. I feel like it's less unpredictable that way, and I have more control.
Be sure to enforce uniqueness of your logins, index them, and if show_by_login can't find the record you have to raise ActiveRecord::RecordNotFound yourself.
def show
#user = User.find(params[:id])
respond_to do |format|
format.html
format.xml { render :xml => #user.to_xml }
end
end
def show_by_login
#user = User.find_by_login(params[:login])
unless #user
raise ActiveRecord::RecordNotFound
end
render :action => 'show'
end
You could use a regular expression:
#user = params[:id] =~ /^\d+$/ ? User.find(params[:id]) : User.find_by_login(params[:id])
So long as you don't allow any logins to consist purely of digits, you could write your own finder/named_scope.
class User < ActiveRecord::Base
named_scope :find_by_id_or_login, lambda {|id_or_login|
{ :conditions => ["id = ? OR login = ?", id_or_login, id_or_login] }
}
end
#user = User.find_by_id_or_login(params[:id])