How to handle exceptions caused by nil:NilClass - ruby-on-rails-3

I have a model called quiz, which has many questions models. I want to add some kind of esception handling so that when the user types in a wrong quiz_id in the URL, an error page would be rendered.
I wrote some helper methods in my QuestionsController to handle the exceptions:
private
def render_error(message)
#error_message = message
render 'error'
end
def active_quizzes_safe
active_quizzes = Quiz.active_quizzes(current_user.id)
render_error('Sorry! The request is invalid! Please log in again!') if active_quizzes.nil?
active_quizzes
end
def active_quiz_safe(quiz_id)
active_quiz = active_quizzes_safe.where(id: quiz_id).first
render_error('The quiz does not exist or you are not allowed to take this quiz!') if active_quiz.blank?
active_quiz
end
And here is the action in QuestionsController which has problems:
def show_quiz
if current_user
#quiz = active_quiz_safe(params[:quiz_id])
#questions = #quiz.questions
end
end
So if the :quiz_id in the URL localhost:3000/MY_URL/:quiz_id is not correct(that is, a record cannot be found), an error page should be rendered by the render_error mothod. However, when I tired a wrong :quiz_id, I got undefined method 'questions' for nil:NilClass. I guess this is because of the #questions = #quiz.questions in show_quiz method.
However, is the execution supposed to halt after the render_error action, which is before #questions = #quiz.questions? Why #questions = #quiz.questions is executed anyway?
In addtion, are there any standard ways to handle nil:NilClass errors like this?
Thank you!!

Look in your public/404.html, public/422.html and public/500.html files. Rails will automatically redirects if error occurs anyway. So I think you don't need to manually handle exceptions, except you have specific case. To test and view this error pages run application in production bundle exec rails s RAILS_ENV=production.

Calling render method does not halt the action. So you should carefully design your action to ensure that you return immediately after rendering. Like this:
def show_quiz
if current_user
active_quizzes = Quiz.active_quizzes(current_user.id)
if active_quizzes.nil?
render_error('Sorry! The request is invalid! Please log in again!')
else
#quiz = active_quizzes_safe.where(id: quiz_id).first
if #quiz.blank?
render_error('The quiz does not exist or you are not allowed to take this quiz!')
else
#questions = #quiz.questions
end
end
end
end
But in this case, I think it's better to use some exception control, like this:
def show_quiz
if current_user
active_quizzes = Quiz.active_quizzes(current_user.id)
#quiz = active_quizzes_safe.find(quiz_id)
#questions = #quiz.questions
end
rescue ActiveRecord::RecordNotFound
render_error 'The quiz does not exist or you are not allowed to take this quiz!'
end

Related

Rails 3: accept all params except a specific value

I have a Rails 3.2.13 Application to maintenance.
Because of authorization rules i want to limit the find(params[:file_registry_id]) method to accept all parameters except 752. (Only user tehen should be able to get it.)
def show
if current_user.tehen?
#file_registry = FileRegistry.find(752)
else
#file_registry = FileRegistry.find(params[:file_registry_id])
end
#rubric = Rubric.find(params[:id])
#rubrics = expanded_rubrics #rubric.ancestors_with_self.collect(&:id)
set_favorites
render :action => 'index'
end
Is there a method available to filter an element (here id 752) from the params hash? Or what's the best way to go?
Simple solution:
def show
#file_registry = get_file_registry
#....
end
private
def get_file_registry
if current_user.tehen?
FileRegistry.find(752)
else
unless params[:file_registry_id] == FORBIDDEN_ID_FOR_GUEST
FileRegistry.find(params[:file_registry_id])
else
false
end
end
end
FORBIDDEN_ID_FOR_GUEST should be defined outside of the controller, for example inside of a initializer.
But I suggest to use a authorization library like CanCan (https://github.com/ryanb/cancan) where you can define permissions for every use case.

How to stop a helper method from applying to a specific controller?

I have a helper_method that allows links to escape from a subdomain. However it is impacting my videos_controller, as it essentially seems to negate the 'current_event' method when not in the events controlller.
I've tried several dozen different ways over the last 4 days to make it so I can still escape my links from the subdomain, but still allow the videos_controller to work.
I think the best way to achieve this is to exclude the videos_controller from the helper method, but I'm not sure how (or if it is actually the best way forward - I'm obviously a noob!) Any suggestions please?! Relevant code below:
module UrlHelper
def url_for(options = nil)
if request.subdomain.present? and request.subdomain.downcase != 'www' and !options.nil? and options.is_a?(Hash) and options.has_key? :only_path and options[:only_path]
options[:only_path] = false
end
super
end
end
Videos_controller
def new
if current_event?
#video = current_event.videos.new
else
#video = Video.new
end
end
def create
if current_event.present?
#video = current_event.videos.new(params[:video])
#video.user_id = current_user.id
key = get_key_from_the_cloud
#video.key = key
else
#video = current_user.videos.new(params[:video])
#video.user_id = current_user.id
key = get_key_from_the_cloud
#video.key = key
end
if #video.save
flash[:success] = "Video uploaded!"
redirect_to root_url(subdomain: => current_event.name)
else
flash[:error] = "#{#video.errors.messages}"
render :new
end
end
current_event method
def current_event
if request.subdomain.present?
#event = Event.find_by_name(request.subdomain)
end
end
Did you take a look at this post yet?
You might want to create a new function test that only does something like
module UrlHelper
def test
puts "Test is called"
end
end
If that works you know its not including that fails but it has to be the method.
Otherwise you know the module is not included and you can narrow down the search.

Local Jump Error No block given (yield) error on find_each

I'm trying to step through a list of records that are retrieved with find_each.
I patterned my controller code on the answer in this stack overflow post, but I'm still getting a "No Block Given (Yield)" error.
I'm just getting started in Ruby and Rails and I haven't yet found a full fledged explanation (lots of basic examples though) of blocks and yield that gives me what I need.
My code looks like this:
def select_save
#class = params[:class]
#student_id = params[:id]
#class.each do |id|
old_subject = Subject.find(id)
new_subject = old_subject.dup
new_subject.student_id = #student_id
new_subject.save
Assignment.find_each.where(:subject_id => id) do |assignments|
assignments.each do |a|
new_assignment = a.dup
new_assignment.subject_id = new_subject.id
new_assignment.save
end
end
end
respond_to do |format|
format.html { redirect_to #student, :notice => 'Subject and assignments created.' }
end
end
and the error points to the line with find_each.
I know I need a block to yield to, but how exactly that would look in this particular case escapes me.
Thanks for any suggestions.
You're passing a block to where and no block to find_each. You can't do that. You need to reverse find_each and where on this line, the order is important as the block is passed to the last method invoked:
Assignment.find_each.where(:subject_id => id) do |assignments|
It should read:
Assignment.where(:subject_id => id).find_each do |assignments|
Your next problem is you're trying to iterate over assignments, which is a single Assignment. find_each is already doing the iteration for you, passing one assignment into the block at a time. The block should read:
Assignment.where(:subject_id => id).find_each do |assignment|
new_assignment = assignment.dup
new_assignment.subject_id = new_subject.id
new_assignment.save
end
I'm going to make the assumption that your Subject has many Assignments, since you have a subject_id inside your Assignment class. If this is the case, the final and most correct way to write your loop would be:
old_subject.assignments.each do |assignment|
new_assignment = assignment.dup
new_assignment.subject_id = new_subject.id
new_assignment.save
end

"Can't dump file" error occurs when using Paperclip attachments with Multi-step form in Rails 3

I'm having the same issue mentioned here:
Multi-step form in Rails 3 with Paperclip attachments
"TypeError in..." "can't dump File"
However, I can't seem to wrap my head around the solution mentioned in the answer:
unless #model.valid?
#model.image.clear
#model.image.queued_for_write.clear
end
Where do I put the above lines?
Here's my code for my controller:
def new
session[:member_params] ||= {}
#member = Member.new(session[:member_params])
#member.current_step = session[:member_step]
end
def create
session[:member_params].deep_merge!(params[:member]) if params[:member]
#member = Member.new(session[:member_params])
#member.current_step = session[:member_step]
if #member.valid?
if params[:back_button]
#member.previous_step
elsif #member.last_step?
#member.save if #member.all_valid?
else
#member.next_step
end
session[:member_step] = #member.current_step
end
if #member.new_record?
render "new"
else
session[:member_step] = session[:member_params] = nil
flash[:notice] = "Personal Data Sheet saved!"
redirect_to #member
end
end
I tried putting it here after the declaration of #member, but no good:
session[:member_params].deep_merge!(params[:member]) if params[:member]
#member = Member.new(session[:member_params])
unless #member.valid?
#member.image.clear
#member.image.queued_for_write.clear
end
P.S.
I thought of appending my question to the question here:
Multi-step form in Rails 3 with Paperclip attachments
But I'm not sure if it's okay to post-follow-up questions on the same issue/question.

Rails 3: Trying to extend Action Mailer with a module

Trying to rewrite an old alias_method_chain to add a filter on outgoing emails, and it isn't working. I'm pretty sure I've leaving something out/missing something, but I don't know what.
This file is in /lib/outgoing_mail_filter.rb, which is loaded with config/initializers/required.rb
Here's the old code that worked under Rails 2:
class ActionMailer::Base
def deliver_with_recipient_filter!(mail = #mail)
unless 'production' == Rails.env
mail.to = mail.to.to_a.delete_if do |to|
!(to.ends_with?('some_domain.com'))
end
end
unless mail.to.blank?
deliver_without_recipient_filter!(mail)
end
end
alias_method_chain 'deliver!'.to_sym, :recipient_filter
end
And here's my current attempt at re-writing it:
class ActionMailer::Base
module RecipientFilter
def deliver(mail = #mail)
super
unless 'production' == Rails.env
mail.to = mail.to.to_a.delete_if do |to|
!(to.ends_with?('some_domain.com'))
end
end
unless mail.to.blank?
deliver(mail)
end
end
end
include RecipientFilter
end
When I run my tests, it doesn't even look like this is being called or anything. Any help is appreciated
I'm using mail_safe to rewrite emails in the development environment, highly recommended. You could look into it for inspiration if it doesn't fit your bill, the code is very simple.
The following code is extracted from /lib/mail_safe/rails3_hook.rb and should do what you want:
require 'mail'
module MailSafe
class MailInterceptor
def self.delivering_email(mail)
# replace the following line with your code
# and don't forget to return the mail object at the end
MailSafe::AddressReplacer.replace_external_addresses(mail) if mail
end
::Mail.register_interceptor(self)
end
end
Alternate version, registering with ActionMailer::Base instead of Mail (thanks to Kevin Whitaker for letting me know it's possible):
module MailSafe
class MailInterceptor
def self.delivering_email(mail)
# replace the following line with your code
# and don't forget to return the mail object at the end
MailSafe::AddressReplacer.replace_external_addresses(mail) if mail
end
::ActionMailer::Base.register_interceptor(self)
end
end