How can I dry this method? - ruby-on-rails

def restore_download_delete_file
begin
case params[:submit]
when "restore"
restore_status = restore_file(params[:file_names])
raise if restore_status != 0
flash[:notice] = "File Successfully Restored."
redirect_to :action => "database_settings"
when "download"
download_status = download_file(params[:file_names])
raise if download_status != 0
when "delete"
delete_status = delete_file(params[:file_names])
raise if delete_status != 0
flash[:notice] = "File Successfully Deleted."
redirect_to :action => "database_settings"
end
rescue Exception => e
flash[:error] = "Error with #{params[:submit]}! Please retry."
redirect_to :action => "database_settings"
end
end
How could I improve this method?

You can clean it up by dividing it into four: one for restore, one for delete, one for download, and one for calling the appropiate one and handling the exceptions.
def restore_download_delete_file
begin
self.send "#{params[:submit]}"
rescue Exception => e
flash[:error] = "Error with #{params[:submit]}! Please retry."
redirect_to :action => "database_settings"
end
end
def restore
restore_status = restore_file(params[:file_names])
raise if restore_status != 0
flash[:notice] = "File Successfully Restored."
redirect_to :action => "database_settings"
end
def download
download_status = download_file(params[:file_names])
raise if download_status != 0
end
def delete
delete_status = delete_file(params[:file_names])
raise if delete_status != 0
flash[:notice] = "File Successfully Deleted."
redirect_to :action => "database_settings"
end
Also, a couple considerations:
Raise proper exceptions, not nil.
Don't rescue all exceptions. Rescue the ones you are raising instead.

Try like this
begin
status = send("#{params[:submit]}_file", params[:file_names])
raise unless status == 0
if params[:submit] == 'restore' || params[:submit] == 'delete'
flash[:notice] = "File Successfully #{params[:submit].capitalize}d"
end
rescue Exception => e
flash[:error] = "Error with #{params[:submit]}! Please retry."
ensure
redirect_to :action => "database_settings" unless params[:submit] == 'download'
end

def restore_download_delete_file
submit = params[:submit]
if not restore_file(params[:file_names]).zero?
flash[:error] = "Error with #{submit}! Please retry."
elsif submit != "download"
flash[:notice] = "File Successfully #{submit.capitalize}d."
else
return
end
redirect_to :action => "database_settings"
end

You can always turn your mega-method into a little interpreter:
private
FILE_CMDS = {
'delete' => {
:action => lambda { |names| delete_file(names) },
:notice => 'File Successfully Deleted.',
:redirect => 'database_settings',
},
'download' => {
:action => lambda { |names| download_file(names) },
},
'restore' => {
:action => lamba { |names| restore_file(names) },
:notice => 'File Successfully Restored.',
:redirect => 'database_settings',
},
}
public
def restore_download_delete_file
begin
cmd = FILE_CMDS[params[:submit]]
status = cmd[:action].call(params[:file_names])
raise if(status != 0)
flash[:notice] = cmd[:notice] if(cmd[:notice])
redirect_to :action => cmd[:redirect] if(cmd[:redirect])
rescue Exception => e
flash[:error] = "Error with #{params[:submit]}! Please retry."
redirect_to :action => "database_settings"
end
end
But I think egarcia's approach makes the most sense; there's really
no need to mash all this stuff into one method. The commonalities
really are quite minimal so four methods in one controller makes
more sense: one action, one method. DRY is just a guideline, it isn't
dogma to be followed at all costs.

Related

Render and/or redirect Ruby

i have login page with 2 sessions client and admin so when client logged in i do redirect to complete the form and when admin logged in i redirect him to dashboard, the problem is :
ActionController::DoubleRenderError (Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".)
def login
#institute = Configuration.find_by_config_key("LogoName")
available_login_authes = FedenaPlugin::AVAILABLE_MODULES.select{|m| m[:name].classify.constantize.respond_to?("login_hook")}
selected_login_hook = available_login_authes.first if available_login_authes.count>=1
if selected_login_hook
authenticated_user = selected_login_hook[:name].classify.constantize.send("login_hook",self)
else
if request.post? and params[:user]
#user = User.new(params[:user])
user = User.find_by_username #user.username
if user.present? and User.authenticate?(#user.username, #user.password)
authenticated_user = user
end
end
end
if authenticated_user.present?
successful_user_login(authenticated_user) and return
elsif authenticated_user.blank? and request.post?
flash[:notice] = "#{t('login_error_message')}"
end
end
private
def successful_user_login(user)
session[:user_id] = user.id
flash[:notice] = "#{t('welcome')}, #{user.first_name} #{user.last_name}!"
redirect_to session[:back_url] || {:controller => 'user', :action => 'dashboard'}
if user.client
redirect_to session[:back_url] || {:controller => 'client', :action => 'complete_registration'}
end
end
end
You said right redirect_to is not a return method, the codes above is executed too. In you successful_user_login if user is client, you are calling redirect_to twice, so you can refactor this method to:
if user.client
redirect_to session[:back_url] || {:controller => 'client', :action => 'complete_registration'}
else
redirect_to session[:back_url] || {:controller => 'user', :action => 'dashboard'}
end
ActionController::DoubleRenderError is a error thrown by Ruby on Rails when a controller action tries to render or redirect a response twice. In your code, the action attempts to redirect the user to both 'user/dashboard' and 'client/complete_registration' depending on the user's status as a client. This will result in a DoubleRenderError. Notice that rendering or redirecting do not returns from the method.
Here are some untested code examples with different approaches:
You can calculate the controller and the action before calling redirect_to:
def successful_user_login(user)
session[:user_id] = user.id
flash[:notice] = "#{t('welcome')}, #{user.first_name} #{user.last_name}!"
controller, action = if user.client
["client", "complete_registration"]
else
["user", "dashboard"]
end
redirect_to session[:back_url] || {controller: controller, action: action}
end
You can also do an if and return after first redirect_to:
def successful_user_login(user)
session[:user_id] = user.id
flash[:notice] = "#{t('welcome')}, #{user.first_name} #{user.last_name}!"
if user.client
redirect_to session[:back_url] || {:controller => 'client', :action => 'complete_registration'}
return
end
redirect_to session[:back_url] || {:controller => 'user', :action => 'dashboard'}
end
Or do an if else:
def successful_user_login(user)
session[:user_id] = user.id
flash[:notice] = "#{t('welcome')}, #{user.first_name} #{user.last_name}!"
if user.client
redirect_to session[:back_url] || {:controller => 'client', :action => 'complete_registration'}
else
redirect_to session[:back_url] || {:controller => 'user', :action => 'dashboard'}
end
end

Rails: Help spot the multiple redirect in this action please

I have been through a number of similar questions but still can't spot the multiple redirects in my action. Can you help me spot it? Also are there any generic tips for avoiding the so called Double Render Error?
The code is as follows:
def st_buy_now_process
#create user in system
encrypted = BCrypt::Password.create(params[:password])
if (User.where("email = ?", params[:email]).exists?)
flash[:alert] = 'User exists. Please try again.'
redirect_to "/st_buy_now"
elsif (User.create(email: params[:email], encrypted_password: encrypted))
begin
customer = Stripe::Customer.create({
:description => params[:email],
:card => {
:number => params[:cc],
:exp_month => params[:exp_1],
:exp_year => params[:exp_2],
:cvc => params[:cvc],
:name => params[:name]
},
:email => params['email']
})
p="#{params['plan']}_#{params['billing-cycle']}"
subscr = customer.subscriptions.create(:plan => p)
user = User.where("email = ?", params[:email]).first
user.payg = ({:cus_id => customer.id, :subscr_id => subscr.id}).to_json
user.plan = p
user.save
rescue Stripe::CardError => e
flash[:alert] = 'Please try again'
redirect_to "/st_buy_now"
end
RestClient.post "https://api:key-nnnn",
:from => "<test#test.com>",
:to => "#{params[:email]}",
:subject => "Welcome to test",
:text => "\n\n\n#{File.read("app/views/misc/mail_sign_up.txt")}\n\n\n"
cookies['logged_in'] = {
:value => '1',
}
cookies['us_id'] = {
:value => params[:email],
}
flash[:notice] = 'Registration successful.'
redirect_to :controller => 'misc', :action => 'create_1'
else
flash[:alert] = 'Please try again'
redirect_to "/st_buy_now"
end
end
The error message highlights the last but one redirect - the line after the "Registration successful." alert.
When you rescue an exception, execution of your code continues on from there as normal, so your redirect_to "/st_buy_now" in your rescue block plus the redirect_to :controller => 'misc', :action => 'create_1' you're getting the error on are your two redirects.
You probably want to throw a return in that rescue block.
What about this, if customer got created proceed further and redirect and if error occur redirect_to "/st_buy_now"
def st_buy_now_process
#create user in system
encrypted = BCrypt::Password.create(params[:password])
if (User.where("email = ?", params[:email]).exists?)
flash[:alert] = 'User exists. Please try again.'
redirect_to "/st_buy_now"
elsif (User.create(email: params[:email], encrypted_password: encrypted))
begin
customer = Stripe::Customer.create({:description => params[:email],:card => {:number => params[:cc], :exp_month => params[:exp_1],:exp_year => params[:exp_2], :cvc => params[:cvc], :name => params[:name]},:email => params['email'] })
p="#{params['plan']}_#{params['billing-cycle']}"
subscr = customer.subscriptions.create(:plan => p)
user = User.where("email = ?", params[:email]).first
user.payg = ({:cus_id => customer.id, :subscr_id => subscr.id}).to_json
user.plan = p
user.save
RestClient.post "https://api:key-nnnn",
:from => "<test#test.com>",
:to => "#{params[:email]}",
:subject => "Welcome to test",
:text => "\n\n\n#{File.read("app/views/misc/mail_sign_up.txt")}\n\n\n"
cookies['logged_in'] = {
:value => '1',
}
cookies['us_id'] = {
:value => params[:email],
}
flash[:notice] = 'Registration successful.'
redirect_to :controller => 'misc', :action => 'create_1'
rescue Stripe::CardError => e
flash[:alert] = 'Please try again'
redirect_to "/st_buy_now"
end
else
flash[:alert] = 'Please try again'
redirect_to "/st_buy_now"
end
end

Phone verification using Twilio in Rails 3

I have written code for Phone verification code but it's not working.
Main problem is, that user can put any number for verification. That means verification_number don't work.
def phone_verification
u = User.find(params[:user_id])
if params[:verification_code]
if u.verification_number.to_s == params[:verification_code].to_s
u.phone_verified = true
u.save
respond_to do |format|
format.js { render :status => :ok }
end
end
else
u.update_attributes(phone: params[:phone], country_code_id: params[:prefix], verification_number: rand(10000))
u.save
# Phone verification
#full_phone = "#{CountryCode.find(params[:prefix]).calling_code}#{params[:phone]}"
number_to_send_to = Rails.env.production? || Rails.env.staging? ? #full_phone : TEST_MOBILE
Rails.logger.debug "-- Verification code: #{u.verification_number}"
#twilio_client = Twilio::REST::Client.new(Mekomy::Application.config.twilio_account_sid, Mekomy::Application.config.twilio_auth_token)
begin
#twilio_client.account.sms.messages.create(
:from => Mekomy::Application.config.twilio_phone_number,
:to => number_to_send_to,
:body => "Please add this verification code when prompt: #{u.verification_number}"
)
respond_to do |format|
format.js { render :status => :ok }
end
rescue
Rails.logger.debug "-- Some error"
end
end
end
Can someone help me to find, where I was wrong?
Thank you all!

AbstractController::DoubleRenderError in TicketsController#update

i get a AbstractController::DoubleRenderError in TicketsController#update.
If i select a particular user, the update is not happening.
def update
#selected_group = Group.find_by_id(params[:Department]) unless params[:Department].nil?
#selected_company = Company.find_by_id(params[:Company]) unless params[:Company].nil?
#ticketnote_content = params[:Ticketnote]
if ((#selected_group != nil) && (#selected_company != nil))
map_group_to_department
map_user_to_staff
update_ticket
if (#response['response'] == "Failed")
flash[:error] = response['err_desc']
redirect_to "/ticket/#{params[:id]}/edit"
return
elsif (#response['response'] == "Success")
#ticketnote_content
if #ticketnote_content != ""
add_note_to_ticket
end
map_assets_findings_tickets
flash[:notice] = "Succesfully updated the ticket"
TicketHistory.create_ticket_history(#assigned_user,#selected_asset,#ticket_params,current_user,#updated_ticket_response,"Updated")
end
else
flash[:error] = "Company or department can't be blank."
redirect_to "/ticket/#{params[:id]}/edit"
return
end
redirect_to :controller => 'tickets' , :action => 'show', :id => params[:id],:test_id => #test,:ticket_id=> params[:ticket_id]
end
As you have already used redirect_to within your if else statement and after execution of if else you are redirecting again, which lead this error(you can use only once within each action). In order to resolve this, I would suggest following solutions(the question was not clear, so I may not be right):
Solution 1: If your last redirect_to is not required then delete it, i.e.
redirect_to :controller => 'tickets' , :action => 'show', :id => params[:id],:test_id => #test,:ticket_id=> params[:ticket_id]
Solution 2: Update with and return in every redirect_to and moving you last redirect_to in your successful response condition(I'm not sure where you want your Ticket#show) , i.e.
def update
#selected_group = Group.find_by_id(params[:Department]) unless params[:Department].nil?
#selected_company = Company.find_by_id(params[:Company]) unless params[:Company].nil?
#ticketnote_content = params[:Ticketnote]
if #selected_group && #selected_company
map_group_to_department
map_user_to_staff
update_ticket
if (#response['response'] == "Failed")
flash[:error] = response['err_desc']
redirect_to "/ticket/#{params[:id]}/edit"
elsif (#response['response'] == "Success")
add_note_to_ticket if #ticketnote_content != ""
map_assets_findings_tickets
flash[:notice] = "Succesfully updated the ticket"
TicketHistory.create_ticket_history(#assigned_user,#selected_asset,#ticket_params,current_user,#updated_ticket_response,"Updated")
redirect_to :controller => 'tickets' , :action => 'show', :id => params[:id],:test_id => #test,:ticket_id=> params[:ticket_id]
end
else
flash[:error] = "Company or department can't be blank."
redirect_to "/ticket/#{params[:id]}/edit" and return
end
end
P.S.: You can use redirect_to and flash[:message] in one line:
redirect_to your_path(params), :notice => "your message"

work with rescue in Rails

I am working with the following piece;
def index
#user = User.find(params[:id])
rescue
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
else
flash[:notice] = "OK"
redirect_to(:action => 'index')
end
Now I either case whether I have a correct ID or not, I am always getting "OK" in my view, what am I doing wrong?
I need that when I have no ID in the DB to show "ERROR". I have also tried to use rescue ActiveRecord::RecordNotFound but same happens.
All help is appreciated.
All code after the end of the rescue block is interpreted only if there are no returns in the rescue block. So you can call return at the end of your rescue block.
def index
begin
#user = User.find(params[:id])
rescue
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
return
end
flash[:notice] = "OK"
redirect_to(:action => 'index')
end
or
def index
#user = User.find(params[:id])
# after is interpret only if no exception before
flash[:notice] = "OK"
redirect_to(:action => 'index')
rescue
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
end
But in your case the better is to use rescue_from or rescue_in_public
like
class UserController < ApplicationController
def rescue_in_public(exception)
flash[:notice] = "ERROR"
redirect_to(:action => 'index')
end
def index
#user = User.find(params[:id])
flash[:notice] = "OK"
redirect_to(:action => 'index')
end
end
But the using of rescue_in_public is not really good advice
Just an overall Rails Rescue answer:
I found this to be very cool:
#user = User.find(params[:id]) rescue ""
If there is no user with that id, then User.find will return nil. Returning nil is not an error case and will not trigger a rescue.

Resources