uk_academic_calendar 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +22 -0
- data/.ruby-version +1 -0
- data/LICENSE.txt +21 -0
- data/README.md +63 -0
- data/Rakefile +12 -0
- data/lib/uk_academic_calendar/autumn_term.rb +18 -0
- data/lib/uk_academic_calendar/core_ext/date.rb +10 -0
- data/lib/uk_academic_calendar/core_ext/integer.rb +9 -0
- data/lib/uk_academic_calendar/core_ext/time.rb +10 -0
- data/lib/uk_academic_calendar/errors/error.rb +5 -0
- data/lib/uk_academic_calendar/errors/invalid_term_bound.rb +25 -0
- data/lib/uk_academic_calendar/errors/invalid_term_end.rb +14 -0
- data/lib/uk_academic_calendar/errors/invalid_term_start.rb +14 -0
- data/lib/uk_academic_calendar/mixins/date_and_time_instance_methods.rb +86 -0
- data/lib/uk_academic_calendar/mixins/succ_slashable.rb +19 -0
- data/lib/uk_academic_calendar/spring_term.rb +18 -0
- data/lib/uk_academic_calendar/summer_term.rb +18 -0
- data/lib/uk_academic_calendar/term.rb +136 -0
- data/lib/uk_academic_calendar/version.rb +5 -0
- data/lib/uk_academic_calendar.rb +63 -0
- metadata +125 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 71d754790b2fdbde926c3ba208b5e860d00d5e1804ea07c74bab60d9478807d4
|
|
4
|
+
data.tar.gz: 4bbc74989d5cffb7e3fda1789a01112e45379474b0b4e6cad8e3645e8f75a507
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 89aadec469b4df7320211d795dbf992c7e55083f486f8767dd6083d236d0f5a3ba8208654861545d2fe0ab60022bccd51ed0deb501cbd64f94ae88f10722f672
|
|
7
|
+
data.tar.gz: 163abbbdde711855ab3e6218278a61d84b58c83025c74dc397dd346662498de48009f7761f3692f36c0b6b661dac7ab892d0f577cb014da8a1a0a3b9feffc622
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 2.6
|
|
3
|
+
NewCops: enable
|
|
4
|
+
|
|
5
|
+
Style/StringLiterals:
|
|
6
|
+
Enabled: true
|
|
7
|
+
EnforcedStyle: single_quotes
|
|
8
|
+
|
|
9
|
+
Style/StringLiteralsInInterpolation:
|
|
10
|
+
Enabled: true
|
|
11
|
+
EnforcedStyle: single_quotes
|
|
12
|
+
|
|
13
|
+
Style/SymbolArray:
|
|
14
|
+
EnforcedStyle: brackets
|
|
15
|
+
|
|
16
|
+
Layout/LineLength:
|
|
17
|
+
Max: 120
|
|
18
|
+
|
|
19
|
+
Metrics/BlockLength:
|
|
20
|
+
Exclude:
|
|
21
|
+
- '*.gemspec'
|
|
22
|
+
- 'spec/**/*.rb'
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.2.2
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Matthew Smith
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# UKAcademicCalendar
|
|
2
|
+
|
|
3
|
+
Designed to assist in Ruby programs dealing with the UK Academic Calendar, i.e. the Sept - Sept academic year, and the 3 'terms' (Autumn, Spring, Summer).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'uk_academic_calendar'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
$ bundle
|
|
16
|
+
|
|
17
|
+
Or install it yourself as:
|
|
18
|
+
|
|
19
|
+
$ gem install uk_academic_calendar
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
require 'uk_academic_calendar'
|
|
25
|
+
|
|
26
|
+
# Assuming today is 20th Jan 2024
|
|
27
|
+
Date.today.academic_year #=> 2023
|
|
28
|
+
Date.today.beginning_of_academic_year #=> 2023-09-01
|
|
29
|
+
Date.today.end_of_academic_year #=> 2024-08-31
|
|
30
|
+
# or...
|
|
31
|
+
Time.now.academic_year #=> 2023
|
|
32
|
+
Time.now.beginning_of_academic_year #=> 2023-09-01 00:00:00 +0100
|
|
33
|
+
Time.now.end_of_academic_year #=> 2024-08-31 23:59:59.999999999 +0100
|
|
34
|
+
|
|
35
|
+
term_now = Date.today.academic_term #=> Spring 2023/2024
|
|
36
|
+
term_now.start_date #=> 2024-01-01
|
|
37
|
+
term_now.end_date #=> 2024-03-31, i.e. Easter Sunday
|
|
38
|
+
term_now.start_date = '2025-01-01' #=> raises #UKAcademicCalendar::InvalidTermStart error
|
|
39
|
+
term_now.to_range #=> (2024-01-01..2024-03-31)
|
|
40
|
+
term_now.all_dates #=> #<SortedSet:{Mon, 01 Jan 2024...}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
See rubydoc [docs](https://www.rubydoc.info/github/m-smiff/uk_academic_calendar/main) for full details on the API (with help from Yard).
|
|
44
|
+
|
|
45
|
+
## Development
|
|
46
|
+
|
|
47
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
48
|
+
|
|
49
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
50
|
+
|
|
51
|
+
## TODO
|
|
52
|
+
|
|
53
|
+
- Further granularity around the 'teachable', and inversely, 'unteachable' days within a term (e.g. adding bank holidays/inset days etc)
|
|
54
|
+
- Considering the above, implementation of e.g., `#teachable_days` and `#non_teachable_days` returning sorted sets of applicable dates
|
|
55
|
+
- Setting of 'contexts' so terms understand what dates to apply for a given 'context' (e.g. a particular local authority)
|
|
56
|
+
|
|
57
|
+
## Contributing
|
|
58
|
+
|
|
59
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/uk_academic_calendar.
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'term'
|
|
4
|
+
|
|
5
|
+
module UKAcademicCalendar
|
|
6
|
+
# Concrete class allowing instantiation of instances of an Autumn term, for a given academic year
|
|
7
|
+
class AutumnTerm < Term
|
|
8
|
+
# @return [Date] Sep 1st
|
|
9
|
+
def nominal_start_date
|
|
10
|
+
@nominal_start_date ||= Date.today.beginning_of_academic_year.change(year: academic_year)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [Date] Dec 31st
|
|
14
|
+
def nominal_end_date
|
|
15
|
+
@nominal_end_date ||= nominal_start_date.end_of_year
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/core_ext/date'
|
|
4
|
+
require_relative '../mixins/date_and_time_instance_methods'
|
|
5
|
+
|
|
6
|
+
class Date
|
|
7
|
+
# includes "UKAcademicCalendar::DateAndTimeInstanceMethods"
|
|
8
|
+
# @!parse include UKAcademicCalendar::DateAndTimeInstanceMethods
|
|
9
|
+
include UKAcademicCalendar::DateAndTimeInstanceMethods
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_support/core_ext/time'
|
|
4
|
+
require_relative '../mixins/date_and_time_instance_methods'
|
|
5
|
+
|
|
6
|
+
class Time
|
|
7
|
+
# includes "UKAcademicCalendar::DateAndTimeInstanceMethods"
|
|
8
|
+
# @!parse include UKAcademicCalendar::DateAndTimeInstanceMethods
|
|
9
|
+
include UKAcademicCalendar::DateAndTimeInstanceMethods
|
|
10
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'error'
|
|
4
|
+
|
|
5
|
+
module UKAcademicCalendar
|
|
6
|
+
# Error class raised when a concrete term class is deemed to have an invalid date-bound
|
|
7
|
+
class InvalidTermBound < Error
|
|
8
|
+
def initialize(date, comparand)
|
|
9
|
+
@date = date
|
|
10
|
+
@comparand = comparand
|
|
11
|
+
super(message)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @return [String]
|
|
15
|
+
def message
|
|
16
|
+
"#{@date} is invalid. Must be #{comparison_qualifier} #{@comparand}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def comparison_qualifier
|
|
22
|
+
@comparand.instance_of?(Range) && 'within'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module UKAcademicCalendar
|
|
4
|
+
# Mixin included in Date and Time classes providing academic year related instance methods
|
|
5
|
+
module DateAndTimeInstanceMethods
|
|
6
|
+
# @return [Integer] integer representing the calendar year within the academic year started in
|
|
7
|
+
# @example
|
|
8
|
+
# Date.new(2024, 8, 31).academic_year #=> 2023
|
|
9
|
+
# Time.new(2024, 9, 1, 12).academic_year #=> 2024
|
|
10
|
+
def academic_year
|
|
11
|
+
return year if yday >= start_yday
|
|
12
|
+
|
|
13
|
+
year - 1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @return [#to_date] instance of self.class representing the beginning of the academic year
|
|
17
|
+
# @example
|
|
18
|
+
# Date.new(2024, 8, 31).beginning_of_academic_year #=> 2023-09-01
|
|
19
|
+
# Time.new(2024, 9, 1, 12).beginning_of_academic_year #=> 2024-09-01 00:00:00 +0100
|
|
20
|
+
def beginning_of_academic_year
|
|
21
|
+
shifted = change(year: academic_year, month: YEAR_OFFSET_START_MONTH, day: 1)
|
|
22
|
+
return shifted unless acts_like? :time
|
|
23
|
+
|
|
24
|
+
shifted.beginning_of_day
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [#to_date] instance of self.class representing the end of the academic year
|
|
28
|
+
# @example
|
|
29
|
+
# Date.new(2024, 8, 31).end_of_academic_year #=> 2024-08-31
|
|
30
|
+
# Time.new(2024, 9, 1, 12).end_of_academic_year #=> 2025-08-31 23:59:59.999999999 +0100
|
|
31
|
+
def end_of_academic_year
|
|
32
|
+
shifted = beginning_of_academic_year.next_year.prev_day
|
|
33
|
+
return shifted unless acts_like? :time
|
|
34
|
+
|
|
35
|
+
shifted.end_of_day
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Integer] integer representing the month of the academic year including self
|
|
39
|
+
# @example
|
|
40
|
+
# Date.new(2024, 8, 31).academic_year_month #=> 12
|
|
41
|
+
# Time.new(2024, 11, 1, 12).academic_year_month #=> 3
|
|
42
|
+
def academic_year_month
|
|
43
|
+
ac_yr_start_month = YEAR_OFFSET_START_MONTH
|
|
44
|
+
((month - ac_yr_start_month) % 12) + 1
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [UKAcademicCalendar::AutumnTerm, UKAcademicCalendar::SpringTerm, UKAcademicCalendar::SummerTerm]
|
|
48
|
+
# term object including self
|
|
49
|
+
# @example
|
|
50
|
+
# Date.new(2024, 8, 31).academic_term #=> Summer 2023/2024
|
|
51
|
+
# Time.new(2024, 11, 1, 12).academic_term #=> Autumn 2024/2025
|
|
52
|
+
def academic_term
|
|
53
|
+
UKAcademicCalendar.term_including self
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [#to_date] instance of self.class representing the beginning of the academic term including self
|
|
57
|
+
# @example
|
|
58
|
+
# Date.new(2024, 2, 25).beginning_of_academic_term #=> 2024-01-01
|
|
59
|
+
# Time.new(2024, 9, 1, 12).beginning_of_academic_term #=> 2024-09-01 00:00:00 +0100
|
|
60
|
+
def beginning_of_academic_term
|
|
61
|
+
term_start = academic_term.start_date
|
|
62
|
+
shifted = change(year: term_start.year, month: term_start.month, day: term_start.day)
|
|
63
|
+
return shifted unless acts_like? :time
|
|
64
|
+
|
|
65
|
+
shifted.beginning_of_day
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @return [#to_date] instance of self.class representing the end of the academic term including self
|
|
69
|
+
# @example
|
|
70
|
+
# Date.new(2024, 11, 25).end_of_academic_term #=> 2024-12-31
|
|
71
|
+
# Time.new(2024, 7, 1, 12).end_of_academic_term #=> 2024-08-31 23:59:999999999 +0100
|
|
72
|
+
def end_of_academic_term
|
|
73
|
+
term_end = academic_term.end_date
|
|
74
|
+
shifted = change(year: term_end.year, month: term_end.month, day: term_end.day)
|
|
75
|
+
return shifted unless acts_like? :time
|
|
76
|
+
|
|
77
|
+
shifted.end_of_day
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def start_yday
|
|
83
|
+
self.class.new(year, YEAR_OFFSET_START_MONTH, 1).yday
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module UKAcademicCalendar
|
|
4
|
+
# Mixin included in Integer class
|
|
5
|
+
module SuccSlashable
|
|
6
|
+
# @param base [Integer]
|
|
7
|
+
# @param slash_succ [Boolean] option specifying whether to leverage the "slash_succ" option
|
|
8
|
+
# @return [String] string where self is concatenated with the value succeeding self, seperated by a forward slash
|
|
9
|
+
# @example
|
|
10
|
+
# 2023.to_s(slash_succ: true) #=> "2023/2024"
|
|
11
|
+
def to_s(base = 10, slash_succ: false)
|
|
12
|
+
if slash_succ
|
|
13
|
+
"#{to_i}/#{to_i.succ}"
|
|
14
|
+
else
|
|
15
|
+
super(base)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'term'
|
|
4
|
+
|
|
5
|
+
module UKAcademicCalendar
|
|
6
|
+
# Concrete class allowing instantiation of instances of an spring term, for a given academic year
|
|
7
|
+
class SpringTerm < Term
|
|
8
|
+
# @return [Date] Jan 1st
|
|
9
|
+
def nominal_start_date
|
|
10
|
+
nominal_end_date.beginning_of_year
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [Date] Easter Sunday
|
|
14
|
+
def nominal_end_date
|
|
15
|
+
Easter.easter(academic_year + 1)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'term'
|
|
4
|
+
|
|
5
|
+
module UKAcademicCalendar
|
|
6
|
+
# Concrete class allowing instantiation of instances of an Summer term, for a given academic year
|
|
7
|
+
class SummerTerm < Term
|
|
8
|
+
# @return [Date] the Monday after Easter Sunday
|
|
9
|
+
def nominal_start_date
|
|
10
|
+
Easter.easter(academic_year + 1).next_day
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [Date] Aug 31st
|
|
14
|
+
def nominal_end_date
|
|
15
|
+
nominal_start_date.end_of_academic_year
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'abstract_class'
|
|
4
|
+
require 'easter'
|
|
5
|
+
require 'forwardable'
|
|
6
|
+
require 'sorted_set'
|
|
7
|
+
|
|
8
|
+
require_relative 'errors/invalid_term_start'
|
|
9
|
+
require_relative 'errors/invalid_term_end'
|
|
10
|
+
|
|
11
|
+
module UKAcademicCalendar
|
|
12
|
+
# Abstract class representing blueprint behaviour for academic terms, bounded by writeable start and end dates
|
|
13
|
+
class Term
|
|
14
|
+
extend AbstractClass
|
|
15
|
+
|
|
16
|
+
extend Forwardable
|
|
17
|
+
# @!method include?
|
|
18
|
+
# Forwards to Range
|
|
19
|
+
# @see Range#include?
|
|
20
|
+
# @!method each
|
|
21
|
+
# Forwards to Range
|
|
22
|
+
# @see Range#each
|
|
23
|
+
def_delegators :to_range, :include?, :each
|
|
24
|
+
|
|
25
|
+
# @param academic_year [Integer]
|
|
26
|
+
# @return [UKAcademicCalendar::AutumnTerm, UKAcademicCalendar::SpringTerm, UKAcademicCalendar::SummerTerm]
|
|
27
|
+
def initialize(academic_year)
|
|
28
|
+
@academic_year = academic_year
|
|
29
|
+
@season = extract_season_from_class_name
|
|
30
|
+
@start_date = nominal_start_date
|
|
31
|
+
@end_date = nominal_end_date
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [Integer] the calendar year within which the term's academic year starts in
|
|
35
|
+
attr_reader :academic_year
|
|
36
|
+
# @overload start_date
|
|
37
|
+
# @overload start_date=(value)
|
|
38
|
+
# Sets value to @start_date, first going through validation
|
|
39
|
+
# @param val [#to_date, nil] the start date of the term. If nil given, falls back to #nominal_start_date
|
|
40
|
+
# @return [Date] assigned date value
|
|
41
|
+
# @raise [UKAcademicCalendar::InvalidTermStart, UKAcademicCalendar::InvalidTermEnd] if the assigned date is
|
|
42
|
+
# considered invalid
|
|
43
|
+
attr_accessor :start_date
|
|
44
|
+
# @overload end_date
|
|
45
|
+
# @overload end_date=(value)
|
|
46
|
+
# Sets value to @end_date, first going through validation
|
|
47
|
+
# @param val [#to_date, nil] the end date of the term. If nil given, falls back to #nominal_end_date
|
|
48
|
+
# @return [Date] assigned date value
|
|
49
|
+
# @raise [UKAcademicCalendar::InvalidTermStart, UKAcademicCalendar::InvalidTermEnd] if the assigned date is
|
|
50
|
+
# considered invalid
|
|
51
|
+
attr_accessor :end_date
|
|
52
|
+
# @return [Symbol] the name of the season the term spans
|
|
53
|
+
attr_reader :season
|
|
54
|
+
|
|
55
|
+
# @return [Integer] the integer hash value for self
|
|
56
|
+
def hash
|
|
57
|
+
[self.class, academic_year, start_date, end_date].hash
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @param other [#hash] any object that responds to `#hash`
|
|
61
|
+
# @return [Boolean] true if self and other are of the same class, academic_year, and have eql start and end dates.
|
|
62
|
+
# Otherwise returns false.
|
|
63
|
+
def eql?(other)
|
|
64
|
+
hash == other.hash
|
|
65
|
+
end
|
|
66
|
+
alias == eql?
|
|
67
|
+
|
|
68
|
+
# @return [String] prettified string describing the term, e.g. "Summer 2023/2024"
|
|
69
|
+
# @example
|
|
70
|
+
# term = UKAcademicCalendar::SummerTerm 2023
|
|
71
|
+
# term.to_s #=> "Summer 2023/2024"
|
|
72
|
+
def to_s
|
|
73
|
+
"#{season.to_s.titleize} #{academic_year.to_s(slash_succ: true)}"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def inspect
|
|
77
|
+
to_s
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def start_date=(val) # rubocop:disable Lint/DuplicateMethods
|
|
81
|
+
@start_date = validate_date(val&.to_date, :start)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def end_date=(val) # rubocop:disable Lint/DuplicateMethods
|
|
85
|
+
@end_date = validate_date(val&.to_date, :end)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @return [Range] range bounded by start and end dates, end inclusive
|
|
89
|
+
def to_range
|
|
90
|
+
start_date..end_date
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# @return [SortedSet<Date>] sorted set of dates making up the term
|
|
94
|
+
def all_dates
|
|
95
|
+
SortedSet.new(to_range.to_a)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def extract_season_from_class_name
|
|
101
|
+
self.class.to_s.demodulize[/(.*)Term\z/, 1].downcase.to_sym
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def validate_date(date, bound)
|
|
105
|
+
return public_send(:"nominal_#{bound}_date") unless date.present?
|
|
106
|
+
|
|
107
|
+
# First validate the date is within bounds...
|
|
108
|
+
validate_within_bounds(date, nominal_start_date..nominal_end_date, bound)
|
|
109
|
+
# ...then validate the dates are in acceptable (i.e. start < end) sequence
|
|
110
|
+
validate_sequence(date, bound)
|
|
111
|
+
|
|
112
|
+
date
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def validate_within_bounds(date, range, date_bound)
|
|
116
|
+
return if date.in? range
|
|
117
|
+
|
|
118
|
+
raise date_error_class(date_bound).new(date, range)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def validate_sequence(date, date_bound)
|
|
122
|
+
operator, comparand = date_bound == :start ? [:<, :end_date] : [:>, :start_date]
|
|
123
|
+
# Assuming this is for :start_date, return if #start_date == nil OR date < #end_date
|
|
124
|
+
return if send(comparand).nil? || date.send(operator, send(comparand))
|
|
125
|
+
|
|
126
|
+
raise date_error_class(date_bound).new(date, send(comparand))
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def date_error_class(date_bound)
|
|
130
|
+
case date_bound
|
|
131
|
+
when :start then InvalidTermStart
|
|
132
|
+
when :end then InvalidTermEnd
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'uk_academic_calendar/version'
|
|
4
|
+
|
|
5
|
+
require 'uk_academic_calendar/core_ext/date'
|
|
6
|
+
require 'uk_academic_calendar/core_ext/time'
|
|
7
|
+
require 'uk_academic_calendar/core_ext/integer'
|
|
8
|
+
require 'uk_academic_calendar/autumn_term'
|
|
9
|
+
require 'uk_academic_calendar/spring_term'
|
|
10
|
+
require 'uk_academic_calendar/summer_term'
|
|
11
|
+
|
|
12
|
+
require 'active_support/core_ext/class/subclasses'
|
|
13
|
+
require 'active_support/core_ext/object/inclusion'
|
|
14
|
+
|
|
15
|
+
# Top-level namespace, itself implementing a number of constants and module methods documented below.
|
|
16
|
+
module UKAcademicCalendar
|
|
17
|
+
# The month-number corresponding to the first month within the calendar year that the academic year starts in.
|
|
18
|
+
YEAR_OFFSET_START_MONTH = 9 # September
|
|
19
|
+
# The season-names designating the 3 terms.
|
|
20
|
+
SEASONS = [:autumn, :spring, :summer].freeze
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
# @!method autumn_term
|
|
24
|
+
# @param academic_year [Integer] the calendar year the academic year starts in.
|
|
25
|
+
# @return [UKAcademicCalendar::AutumnTerm] instance of Autumn term.
|
|
26
|
+
|
|
27
|
+
# @!method spring_term
|
|
28
|
+
# @param academic_year [Integer] the calendar year the academic year starts in.
|
|
29
|
+
# @return [UKAcademicCalendar::SpringTerm] instance of Spring term.
|
|
30
|
+
|
|
31
|
+
# @!method summer_term
|
|
32
|
+
# @param academic_year [Integer] the calendar year the academic year starts in.
|
|
33
|
+
# @return [UKAcademicCalendar::SummerTerm] instance of Summer term.
|
|
34
|
+
SEASONS.each do |season|
|
|
35
|
+
define_method(:"#{season}_term") do |academic_year|
|
|
36
|
+
klass = Term.descendants.find { |c| c.to_s.demodulize.downcase.start_with? season.to_s }
|
|
37
|
+
klass.new(academic_year)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @param academic_year [Integer] the calendar year the academic year starts in.
|
|
42
|
+
# @return [Array(UKAcademicCalendar::AutumnTerm, UKAcademicCalendar::SpringTerm, UKAcademicCalendar::SummerTerm)]
|
|
43
|
+
# array containing ordered instances of Autumn, Spring and Summer term.
|
|
44
|
+
def all_terms(academic_year)
|
|
45
|
+
SEASONS.map { |term_name| public_send(:"#{term_name}_term", academic_year) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Convenience method for retrieving the term object including Date.today
|
|
49
|
+
# @return [UKAcademicCalendar::AutumnTerm, UKAcademicCalendar::SpringTerm, UKAcademicCalendar::SummerTerm] instance
|
|
50
|
+
# of Autumn/Spring/Summer term deemed to contain today's date.
|
|
51
|
+
def term_now
|
|
52
|
+
term_including
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @param val [#to_date]
|
|
56
|
+
# @return [UKAcademicCalendar::AutumnTerm, UKAcademicCalendar::SpringTerm, UKAcademicCalendar::SummerTerm] instance
|
|
57
|
+
# of Autumn/Spring/Summer term deemed to contain date param.
|
|
58
|
+
def term_including(val = Date.today)
|
|
59
|
+
date = val.to_date
|
|
60
|
+
all_terms(date.academic_year).find { |term| date.in? term }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: uk_academic_calendar
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Matt
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2024-02-10 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: abstract_class
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activesupport
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '6.1'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '6.1'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: easter
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.2'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.2'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: sorted_set
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.0'
|
|
69
|
+
description: Designed to assist in Ruby programs dealing with the UK Academic Calendar
|
|
70
|
+
email:
|
|
71
|
+
- jetnova@pm.me
|
|
72
|
+
executables: []
|
|
73
|
+
extensions: []
|
|
74
|
+
extra_rdoc_files: []
|
|
75
|
+
files:
|
|
76
|
+
- ".rspec"
|
|
77
|
+
- ".rubocop.yml"
|
|
78
|
+
- ".ruby-version"
|
|
79
|
+
- LICENSE.txt
|
|
80
|
+
- README.md
|
|
81
|
+
- Rakefile
|
|
82
|
+
- lib/uk_academic_calendar.rb
|
|
83
|
+
- lib/uk_academic_calendar/autumn_term.rb
|
|
84
|
+
- lib/uk_academic_calendar/core_ext/date.rb
|
|
85
|
+
- lib/uk_academic_calendar/core_ext/integer.rb
|
|
86
|
+
- lib/uk_academic_calendar/core_ext/time.rb
|
|
87
|
+
- lib/uk_academic_calendar/errors/error.rb
|
|
88
|
+
- lib/uk_academic_calendar/errors/invalid_term_bound.rb
|
|
89
|
+
- lib/uk_academic_calendar/errors/invalid_term_end.rb
|
|
90
|
+
- lib/uk_academic_calendar/errors/invalid_term_start.rb
|
|
91
|
+
- lib/uk_academic_calendar/mixins/date_and_time_instance_methods.rb
|
|
92
|
+
- lib/uk_academic_calendar/mixins/succ_slashable.rb
|
|
93
|
+
- lib/uk_academic_calendar/spring_term.rb
|
|
94
|
+
- lib/uk_academic_calendar/summer_term.rb
|
|
95
|
+
- lib/uk_academic_calendar/term.rb
|
|
96
|
+
- lib/uk_academic_calendar/version.rb
|
|
97
|
+
homepage: https://www.github.com/m-smiff/uk_academic_calendar
|
|
98
|
+
licenses:
|
|
99
|
+
- MIT
|
|
100
|
+
metadata:
|
|
101
|
+
homepage_uri: https://www.github.com/m-smiff/uk_academic_calendar
|
|
102
|
+
documentation_uri: https://www.rubydoc.info/github/m-smiff/uk_academic_calendar/main
|
|
103
|
+
source_code_uri: https://www.github.com/m-smiff/uk_academic_calendar
|
|
104
|
+
changelog_uri: https://www.github.com/m-smiff/uk_academic_calendar
|
|
105
|
+
rubygems_mfa_required: 'true'
|
|
106
|
+
post_install_message:
|
|
107
|
+
rdoc_options: []
|
|
108
|
+
require_paths:
|
|
109
|
+
- lib
|
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
111
|
+
requirements:
|
|
112
|
+
- - ">="
|
|
113
|
+
- !ruby/object:Gem::Version
|
|
114
|
+
version: 2.6.0
|
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
|
+
requirements:
|
|
117
|
+
- - ">="
|
|
118
|
+
- !ruby/object:Gem::Version
|
|
119
|
+
version: '0'
|
|
120
|
+
requirements: []
|
|
121
|
+
rubygems_version: 3.5.5
|
|
122
|
+
signing_key:
|
|
123
|
+
specification_version: 4
|
|
124
|
+
summary: UK Academic Calendar
|
|
125
|
+
test_files: []
|