require 'osu_term/version' module OSU # A simple helper class to turn strms into readable human names. It can also # determine if an strm took place during quarters, semesters, or during the # quarter-to-semester transition. It can also calculate the next strm. class Term include Comparable attr_reader :strm # this strm was used to to transition from quarters to semesters QUARTER_TO_SEMESTER_STRM = '1123'.freeze # Initializes a new OsuTerm based on the strm passed in. # # @param args [Hash] A hash of arguments to pass into the constructor. # @option args [String] :strm # # The osu term's strm number. This number has several requirements: # 1. The strm must be a string that's 4 digits in length. # 2. The first character must be 0 or 1. 0 = 1900-1999, 1 = 2000 and beyond # 3. strm's 2nd and 3rd char represents last two digits of the year # 4. the strm's last digit: # - quarter: 0 (winter), 2 (Spring), 4 (Summer), or 8 (Autumn) # - semester: 2 (Spring), 4 (Summer), or 8 (Autumn) # # For example, '1152' stands for "Spring 2015". # 1 = 2000 # 15 = 2015 # 2 = Spring # # @return [OsuTerm] A new OSU::Term object. # @raise [ArgumentError] Raised when the strm is malformed. def initialize(strm:) strm = strm.to_s raise ArgumentError, 'strm must be 4 digits in length' unless FOUR_DIGITS.match?(strm) raise ArgumentError, 'strm must start with 0 or 1' unless CENTURIES.match?(strm) @strm = strm if strm < QUARTER_TO_SEMESTER_STRM extend(Quarter) raise ArgumentError, 'strm must end in 0, 2, 4, or 8' unless Quarter::SEASONS.match?(strm) elsif strm == QUARTER_TO_SEMESTER_STRM extend(TransitionTerm) else extend(Semester) raise ArgumentError, 'strm must end in 2, 4, or 8' unless Semester::SEASONS.match?(strm) end end def name names[season_number].fetch(:full) end def abbr_name names[season_number].fetch(:abbr) end def abbr_year strm[1..2] end def year @year ||= "#{century}#{abbr_year}" end def description @description ||= "#{name} #{year} #{type}" end def to_s description end def abbreviated @abbreviated ||= "#{abbr_name} '#{abbr_year} #{abbr_type}" end def <=>(other) strm <=> other.strm rescue NoMethodError nil end def next_strm if transition_semester? '1124' else next_season_number = next_season.fetch(season_number) case season_number when 0, 2, 4 century_and_decade = strm[0..2] "#{century_and_decade}#{next_season_number}" when 8 century_indicator = strm[0].to_i next_year = decade + 1 if next_year == 100 [century_indicator + 1, 0, 0, next_season_number].join else century_indicator.to_s + next_year.to_s.rjust(2, '0') + next_season_number.to_s end end end end def next self.class.new(strm: next_strm) end private FOUR_DIGITS = /^\d{4}$/ CENTURIES = /(0|1)/ def season_number @season_number ||= strm[-1].to_i end def century @century ||= centuries[strm[0].to_i] end def decade abbr_year.to_i end end # Logic for OSU quarters module Quarter SEASONS = /(0|2|4|8)$/ # Winter, Spring, Summer, Autumn def type 'Quarter' end def abbr_type 'Qtr' end def quarter? true end def semester? false end def transition_semester? false end private def names { 0 => { full: 'Winter', abbr: 'Wi' }, 2 => { full: 'Spring', abbr: 'Sp' }, 4 => { full: 'Summer', abbr: 'Su' }, 8 => { full: 'Autumn', abbr: 'Au' } } end def centuries { 0 => '19', 1 => '20' } end def next_season { 0 => 2, 2 => 4, 4 => 8, 8 => 0 } end end # Logic for OSU transition terms module TransitionTerm SEASONS = /(3)$/ # Transition def type 'Semester Status Recalculation' end def abbr_type 'Sem' end def quarter? false end def semester? true end def transition_semester? true end private def names Hash.new(full: 'Spring', abbr: 'Sp') end def centuries Hash.new('20') end end # Logic for OSU semesters module Semester SEASONS = /(2|4|8)$/ # Spring, Summer, Autumn def type strm[-1] == SUMMER ? 'Term' : 'Semester' end def abbr_type strm[-1] == SUMMER ? 'Trm' : 'Sem' end def quarter? false end def semester? true end def transition_semester? false end private SUMMER = '4'.freeze def names { 2 => { full: 'Spring', abbr: 'Sp' }, 4 => { full: 'Summer', abbr: 'Su' }, 8 => { full: 'Autumn', abbr: 'Au' } } end def centuries Hash.new('20') end def next_season { 2 => 4, 4 => 8, 8 => 2 } end end end