uk_academic_calendar 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|