User:Spurush
E2368. Reimplement due_date.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.
Project Changes
In this project, the following specific changes were implemented:
- Removed methods used exclusively in test files.
- Created a new file, due_date_helper.rb, to handle specific functionalities.
- Moved pertinent methods from the model class to the new helper class.
- Refactored each method to comply with DRY (Don't Repeat Yourself), SRP (Single Responsibility Principle), and other Ruby conventions.
- Renamed methods to accurately describe their responsibilities.
- Addressed identified code smells and issues using Code Climate analysis.
These changes aimed to enhance code quality, organization, and adherence to best practices in the Expertiza project.
Current Implementation of DueDate Class
The current implementation of the `DueDate` class can be summarized in three key points:
Deadline and Time Handling
The class manages due dates for various elements within an assignment. It includes functionalities to:
- Determine the current due date based on time comparisons.
- Check for teammate review allowance based on due dates.
- Set flags and validate due date formats.
Assignment and Topic-Based Due Date Retrieval
The class retrieves the next due date for assignments or topics. It includes logic to:
- Handle staggered deadlines for topics and fallback procedures for unavailable topic-specific due dates.
- Retrieve assignment-specific due dates considering the current time.
Functionality for Assignment Duplication
The class offers methods to handle due date duplication between assignments. It includes functionalities to:
- Copy due dates from one assignment to another.
- Set due dates for assignments based on various parameters.
- Calculate assignment-specific rounds for responses like author feedback, quizzes, and reviews based on deadlines.
This `DueDate` class serves as a central component within the Expertiza project for managing and handling due dates, validation, and retrieval concerning assignment and deadline management.
Improvements in the New Implementation of DueDate Class
The changes made in the new implementation of the `DueDate` class addressed several issues, along with their corresponding solutions:
Removed methods used only in the test files
- Problem: The code contained methods tailored exclusively for testing, leading to unnecessary clutter and potential confusion within the primary codebase.
- Solution: Removal of test-specific methods streamlined the code, enhancing focus and clarity, ensuring that the main code is more comprehensible for developers working on functional components.
The method `set_duedate` was originally housed in the `due_date.rb` file and was later relocated to the `due_date_spec.rb` file within the Expertiza project. This method is responsible for setting a due date.
The relocated `set_duedate` method performs the following tasks:
def self.set_duedate(duedate, deadline, assign_id, max_round) submit_duedate = DueDate.new(duedate) submit_duedate.deadline_type_id = deadline submit_duedate.parent_id = assign_id submit_duedate.round = max_round submit_duedate.save end
Created a new due_date_helper.rb file
- Problem: The primary model class had an abundance of methods, including helper functions, affecting readability and maintainability.
- Solution: The establishment of a dedicated file for due date helper methods segregated the code, enabling a clear distinction between model-specific functions and those supporting due date functionalities, leading to improved modularity and organization.
Moved relevant methods from the model class to the helper class
- Problem: The model class contained methods better suited for helper functions, violating the Single Responsibility Principle and causing complexity.
- Solution: Relocating pertinent methods to the helper class aligned the code with the Single Responsibility Principle, enhancing maintainability and organization by ensuring distinct purposes for each class.
Below are the contents of the newly implemented file app/helpers/due_date_helper.rb
# frozen_string_literal: true # app/helpers/due_date_helper.rb # This module contains helper methods related to due dates. module DueDateHelper def self.deadline_sort(due_dates) # Override the comparator operator to sort due dates by due_at due_dates.sort { |m1, m2| m1.due_at.to_i <=> m2.due_at.to_i } end ...
Refactored each method according to DRY, SRP, and other Ruby conventions
- Problem: Methods were potentially repetitive, non-optimized, or didn't adhere to best practices, causing inefficiencies and confusion.
- Solution: Method refactoring according to DRY, SRP, and Ruby conventions optimized the code for clarity, efficiency, and maintainability, ensuring each method has a clear purpose and follows standard practices.
Before refactoring
def self.done_in_assignment_round(assignment_id, response) # for author feedback, quiz, teammate review and metareview, Expertiza only support one round, so the round # should be 1 return 0 if ResponseMap.find(response.map_id).type != 'ReviewResponseMap' due_dates = DueDate.where(parent_id: assignment_id) # sorted so that the earliest deadline is at the first sorted_deadlines = deadline_sort(due_dates) due_dates.reject { |due_date| due_date.deadline_type_id != 1 && due_date.deadline_type_id != 2 } round = 1 sorted_deadlines.each do |due_date| break if response.created_at < due_date.due_at round += 1 if due_date.deadline_type_id == 2 end round end
After refactoring
def self.calculate_assignment_round(assignment_id, response) return 0 unless ResponseMap.find(response.map_id).type == 'ReviewResponseMap' due_dates = DueDate.where(parent_id: assignment_id) sorted_deadlines = deadline_sort(due_dates) determine_assignment_round(response, sorted_deadlines) end def self.determine_assignment_round(response, sorted_due_dates) round = 1 sorted_due_dates.each do |due_date| break if response.created_at < due_date.due_at round += 1 if due_date.deadline_type_id == 2 end round end
Renamed methods to describe their responsibility
- Problem: Method names might not have accurately described their functionality, leading to confusion for developers.
- Solution: Method renaming with descriptive names clarified their purpose, improving code readability and comprehension for developers to understand method functionalities without deep code inspection.
Before Renaming
def self.done_in_assignment_round(assignment_id, response) .. def self.set_duedate(duedate, deadline, assign_id, max_round) .. def self.get_next_due_date(assignment_id, topic_id = nil)
def self.calculate_assignment_round(assignment_id, response) .. def set_due_date(duedate, deadline, assign_id, max_round) .. def self.find_next_topic_due_date(assignment_id, topic_id)
Fixed code smells using Code Climate
- Problem: Code Climate identified issues like inefficiencies, redundancies, or style violations, potentially impacting code quality and maintainability.
- Solution: Addressing Code Climate's feedback resolved potential issues, leading to an optimized, robust, and maintainable codebase aligning with higher standards.
Before refactoring, the same block of code given below was present in due_date_spec.rb and due_date_helper_spec.rb
before(:each) do @deadline_type = build(:deadline_type) @deadline_right = build(:deadline_right) @assignment_due_date = build(:assignment_due_date, deadline_type: @deadline_type, submission_allowed_id: @deadline_right.id, review_allowed_id: @deadline_right.id, review_of_review_allowed_id: @deadline_right.id, due_at: '2015-12-30 23:30:12') @due_dates = [] 10.times.each do |n| date = if n == 1 || n == 9 nil else Time.zone.now - 60 * n end @due_dates << build(:assignment_due_date, due_at: date) end end
After refactoring, we created a new file spec/support/shared_contexts.rb to house the common block of code.
RSpec.shared_context 'with_deadline_setup' do before(:each) do @deadline_type = build(:deadline_type) @deadline_right = build(:deadline_right) @assignment_due_date = build( :assignment_due_date, deadline_type: @deadline_type, submission_allowed_id: @deadline_right.id, review_allowed_id: @deadline_right.id, review_of_review_allowed_id: @deadline_right.id, due_at: '2015-12-30 23:30:12' ) @due_dates = [] 10.times.each do |n| date = if n == 1 || n == 9 nil else Time.zone.now - 60 * n end @due_dates << build(:assignment_due_date, due_at: date) end end end
By recognizing these problems and applying their respective solutions, the new implementation of the `DueDate` class aims to enhance code quality, readability, and maintainability within the Expertiza project.
Tests
Created a new file spec/helpers/due_date_helper_spec.rb to handle all the validation for due_date_helper module. Below are some examples -
require 'support/shared_contexts' describe DueDateHelper do def set_due_date(duedate, deadline, assign_id, max_round) ActiveRecord::Base.transaction do submit_duedate = DueDate.new(duedate) submit_duedate.deadline_type_id = deadline submit_duedate.parent_id = assign_id submit_duedate.round = max_round submit_duedate.save end end include_context 'with_deadline_setup' it 'sort duedate records' do sorted_due_dates = @due_dates expect(sorted_due_dates.each_cons(2).all? { |m1, m2| (m1.due_at <=> m2.due_at) != 1 }).to eql false sorted_due_dates = DueDateHelper.deadline_sort(@due_dates) expect(sorted_due_dates.each_cons(2).all? { |m1, m2| (m1.due_at <=> m2.due_at) != 1 }).to eql true end describe '#calculate_assignment_round' do it 'return 0 when no response map' do response = ReviewResponseMap.create response.type = 'ResponseMap' response.save expect(DueDateHelper.calculate_assignment_round(1, response)).to eql 0 end ...
References
- Expertiza on GitHub
- GitHub Project Repository Fork
- The live Expertiza website
- Demo link
- Expertiza project documentation wiki
- Rspec Documentation
- Clean Code: A handbook of agile software craftsmanship. Author: Robert C Martin