CSC/ECE 517 Fall 2018 E1836 Refactor quiz questionnaires controller.rb
E1836. Refactoring quiz_questionnaires_controller.rb
This page provides a description of the Expertiza based OSS project.
About Expertiza
Expertiza is an open source project based on Ruby on Rails framework. Expertiza allows the instructor to create new assignments and customize new or existing assignments. It also allows the instructor to create a list of topics the students can sign up for. Students can form teams in Expertiza to work on various projects and assignments. Students can also peer review other students' submissions. Expertiza supports submission across various document types, including the URLs and wiki pages.
Problem Statement
The following tasks were accomplished in this project:
- Created quiz_questionnaire_controller.rb
- Moved view_quiz, new_quiz, create_quiz_questionnaire, edit_quiz and update_quiz from questionnaires_controller.rb, to quiz_questionnaires_controller.rb, and renamed it as view, new, create, edit and update respectively.
- Refactored the the long methods in questionnaires_controller.rb, such as create and create_questionnaire, update quiz, valid quiz, etc.
- Replaced switch statements in questionnaires_controller.rb with subclass methods.
- Created models for the subclasses.
- Removed hardcoded parameters, such as save_choice method.
- Appropriate tests were written to test the code.
Implementation
Problem 1: Create quiz_questionnaire_controller.rb and move some of the methods in the questionnaires_controller.rb to the new file created.
- Solution: The methods view_quiz, new_quiz, create_quiz, edit_quiz and update_quiz in the questionnaires_controller.rb were the methods identified and moved into the quiz_questionnaire_controller.rb. They were respectively renamed as view, new, create, edit and update. The implementation of the newly created quiz_questionnaire_controller.rb can be seen below.
class QuizQuestionnairesController < QuestionnairesController #========================================================================================================= # Separate methods for quiz questionnaire #========================================================================================================= # View a quiz questionnaire def view @questionnaire = Questionnaire.find(params[:id]) @participant = Participant.find(params[:pid]) # creating an instance variable since it needs to be sent to submitted_content/edit render :view end # define a new quiz questionnaire # method invoked by the view def new valid_request = true @assignment_id = params[:aid] # creating an instance variable to hold the assignment id @participant_id = params[:pid] # creating an instance variable to hold the participant id assignment = Assignment.find(@assignment_id) if !assignment.require_quiz? # flash error if this assignment does not require quiz flash[:error] = "This assignment does not support the quizzing feature." valid_request = false else team = AssignmentParticipant.find(@participant_id).team if team.nil? # flash error if this current participant does not have a team flash[:error] = "You should create or join a team first." valid_request = false else if assignment.topics? && team.topic.nil? # flash error if this assignment has topic but current team does not have a topic flash[:error] = "Your team should have a topic." valid_request = false end end end if valid_request && Questionnaire::QUESTIONNAIRE_TYPES.include?(params[:model]) @questionnaire = Object.const_get(params[:model]).new @questionnaire.private = params[:private] @questionnaire.min_question_score = 0 @questionnaire.max_question_score = 1 render :new else redirect_to controller: 'submitted_content', action: 'view', id: params[:pid] end end # seperate method for creating a quiz questionnaire because of differences in permission def create valid = QuizQuestionnaire.valid if valid.eql?("valid") @questionnaire = Object.const_get(params[:questionnaire][:type]).new(questionnaire_params) # TODO: check for Quiz Questionnaire? if @questionnaire.type == "QuizQuestionnaire" # checking if it is a quiz questionnaire participant_id = params[:pid] # creating a local variable to send as parameter to submitted content if it is a quiz questionnaire @questionnaire.min_question_score = 0 @questionnaire.max_question_score = 1 author_team = AssignmentTeam.team(Participant.find(participant_id)) @questionnaire.instructor_id = author_team.id # for a team assignment, set the instructor id to the team_id @successful_create = true save save_choices @questionnaire.id flash[:note] = "The quiz was successfully created." if @successful_create == true redirect_to controller: 'submitted_content', action: 'edit', id: participant_id else # if it is not a quiz questionnaire @questionnaire.instructor_id = Ta.get_my_instructor(session[:user].id) if session[:user].role.name == "Teaching Assistant" save redirect_to controller: 'tree_display', action: 'list' end else flash[:error] = valid.to_s redirect_to :back end end # edit a quiz questionnaire def edit @questionnaire = Questionnaire.find(params[:id]) if !@questionnaire.taken_by_anyone? render :edit else flash[:error] = "Your quiz has been taken by some other students, you cannot edit it anymore." redirect_to controller: 'submitted_content', action: 'view', id: params[:pid] end end # save an updated quiz questionnaire to the database def update @questionnaire = Questionnaire.find(params[:id]) if @questionnaire.nil? redirect_to controller: 'submitted_content', action: 'view', id: params[:pid] return end if params['save'] && params[:question].try(:keys) @questionnaire.update_attributes(questionnaire_params) for qid in params[:question].keys @question = Question.find(qid) @question.txt = params[:question][qid.to_sym][:txt] @question.save @quiz_question_choices = QuizQuestionChoice.where(question_id: qid) i = 1 for quiz_question_choice in @quiz_question_choices type = @question.type id = @question.id QuizQuestionnaire.change_question_types(quiz_question_choice, type, id, i) i += 1 end end end redirect_to controller: 'submitted_content', action: 'view', id: params[:pid] end end
Problem 2: Refactor the long methods in questionnaires_controller.rb, such as create and create_questionnaire, update quiz, valid quiz, etc.
- Solution:
a. Deleted the create_questionnaire method and replaced the call to it in create_quiz_questionnaire with the code of create_questionnaire and is later moved to quiz_questionnaires_controller.rb and the code snippet below is used.
def create valid = QuizQuestionnaire.valid if valid.eql?("valid") @questionnaire = Object.const_get(params[:questionnaire][:type]).new(questionnaire_params) # TODO: check for Quiz Questionnaire? if @questionnaire.type == "QuizQuestionnaire" # checking if it is a quiz questionnaire participant_id = params[:pid] # creating a local variable to send as parameter to submitted content if it is a quiz questionnaire @questionnaire.min_question_score = 0 @questionnaire.max_question_score = 1 author_team = AssignmentTeam.team(Participant.find(participant_id)) @questionnaire.instructor_id = author_team.id # for a team assignment, set the instructor id to the team_id @successful_create = true save save_choices @questionnaire.id flash[:note] = "The quiz was successfully created." if @successful_create == true redirect_to controller: 'submitted_content', action: 'edit', id: participant_id else # if it is not a quiz questionnaire @questionnaire.instructor_id = Ta.get_my_instructor(session[:user].id) if session[:user].role.name == "Teaching Assistant" save redirect_to controller: 'tree_display', action: 'list' end else flash[:error] = valid.to_s redirect_to :back end end
b. Moved the large block of for and if statements into method called change_question_types in the quiz_questionnaire model.
def change_question_types(quiz_question_choice, type, id, i) if type == "MultipleChoiceCheckbox" if params[:quiz_question_choices][id.to_s][type][i.to_s] quiz_question_choice.update_attributes(iscorrect: params[:quiz_question_choices][id.to_s][type][i.to_s][:iscorrect], txt: params[:quiz_question_choices][id.to_s][type][i.to_s][:txt]) else quiz_question_choice.update_attributes(iscorrect: '0', txt: params[:quiz_question_choices][quiz_question_choice.id.to_s][:txt]) end end if type == "MultipleChoiceRadio" if params[:quiz_question_choices][id.to_s][type][:correctindex] == i.to_s quiz_question_choice.update_attributes(iscorrect: '1', txt: params[:quiz_question_choices][id.to_s][type][i.to_s][:txt]) else quiz_question_choice.update_attributes(iscorrect: '0', txt: params[:quiz_question_choices][id.to_s][type][i.to_s][:txt]) end end if type == "TrueFalse" if params[:quiz_question_choices][id.to_s][type][1.to_s][:iscorrect] == "True" # the statement is correct if quiz_question_choice.txt == "True" quiz_question_choice.update_attributes(iscorrect: '1') # the statement is correct so "True" is the right answer else quiz_question_choice.update_attributes(iscorrect: '0') end else # the statement is not correct if quiz_question_choice.txt == "True" quiz_question_choice.update_attributes(iscorrect: '0') else quiz_question_choice.update_attributes(iscorrect: '1') # the statement is not correct so "False" is the right answer end end end end
c. Replaced large block of for and if statements in update with a call to the new change_question_types method.
for quiz_question_choice in @quiz_question_choices type = @question.type id = @question.id QuizQuestionnaire.''change_question_types''(quiz_question_choice, type, id, i) i += 1 end
Problem 3: Replace switch statements with subclasses methods and create models for the subclasses.
- Solution: Moved the switch-case statement from create in questionaire_controller.rb into a method called set_display_type in questionaire model. The following is the code snippet that shows the same.
def set_dispay_type(display_type) case display_type when 'Review' display_type = 'Review' when 'Metareview' display_type = 'Metareview' when 'AuthorFeedback' display_type = 'Author%Feedback' when 'CourseSurvey' display_type = 'Course%Survey' when 'TeammateReview' display_type = 'Teammate%Review' when 'GlobalSurvey' display_type = 'Global%Survey' when 'AssignmentSurvey' display_type = 'Assignment%Survey' when 'Bookmarkrating' display_type = 'Bookmarkrating' end end
Problem 4: Remove hardcoded parameters, such as save_choice method
- Solution: Hardcoded parameters are removed and necessary constants are defined.