timeboss 0.0.8 → 0.2.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.gitignore +5 -3
  5. data/.yardopts +1 -0
  6. data/CODE_OF_CONDUCT.md +76 -0
  7. data/README.md +29 -11
  8. data/lib/timeboss.rb +4 -0
  9. data/lib/timeboss/calendar.rb +17 -0
  10. data/lib/timeboss/calendar/day.rb +18 -6
  11. data/lib/timeboss/calendar/half.rb +6 -2
  12. data/lib/timeboss/calendar/month.rb +6 -2
  13. data/lib/timeboss/calendar/period.rb +25 -11
  14. data/lib/timeboss/calendar/quarter.rb +6 -2
  15. data/lib/timeboss/calendar/support/formatter.rb +4 -2
  16. data/lib/timeboss/calendar/support/month_basis.rb +1 -0
  17. data/lib/timeboss/calendar/support/{month_based.rb → monthly_unit.rb} +22 -18
  18. data/lib/timeboss/calendar/support/navigable.rb +72 -0
  19. data/lib/timeboss/calendar/support/shiftable.rb +218 -28
  20. data/lib/timeboss/calendar/support/translatable.rb +92 -0
  21. data/lib/timeboss/calendar/support/unit.rb +29 -0
  22. data/lib/timeboss/calendar/waypoints.rb +5 -80
  23. data/lib/timeboss/calendar/waypoints/absolute.rb +113 -0
  24. data/lib/timeboss/calendar/waypoints/relative.rb +267 -0
  25. data/lib/timeboss/calendar/week.rb +26 -18
  26. data/lib/timeboss/calendar/year.rb +5 -5
  27. data/lib/timeboss/calendars.rb +15 -0
  28. data/lib/timeboss/calendars/broadcast.rb +4 -0
  29. data/lib/timeboss/calendars/gregorian.rb +24 -0
  30. data/lib/timeboss/version.rb +1 -1
  31. data/spec/calendar/day_spec.rb +1 -1
  32. data/spec/calendar/support/{month_based_spec.rb → monthly_unit_spec.rb} +24 -8
  33. data/spec/calendar/week_spec.rb +11 -14
  34. data/spec/calendars/broadcast_spec.rb +27 -7
  35. data/timeboss.gemspec +1 -0
  36. metadata +28 -5
@@ -4,41 +4,49 @@ require_relative './support/unit'
4
4
  module TimeBoss
5
5
  class Calendar
6
6
  class Week < Support::Unit
7
- attr_reader :year_index, :index
8
-
9
- def initialize(calendar, year_index, index, start_date, end_date)
7
+ def initialize(calendar, start_date, end_date)
8
+ raise UnsupportedUnitError unless calendar.supports_weeks?
10
9
  super(calendar, start_date, end_date)
11
- @year_index = year_index
12
- @index = index
13
10
  end
14
11
 
12
+ # Get a simple representation of this week.
13
+ # @return [String] (e.g. "2020W32")
15
14
  def name
16
15
  "#{year_index}W#{index}"
17
16
  end
18
17
 
18
+ # Get a "pretty" representation of this week.
19
+ # @return [String] (e.g. "Week of August 3, 2020")
19
20
  def title
20
21
  "Week of #{start_date.strftime('%B %-d, %Y')}"
21
22
  end
22
23
 
24
+ # Get a stringified representation of this week.
25
+ # @return [String] (e.g. "2020W32: 2020-08-03 thru 2020-08-09")
23
26
  def to_s
24
27
  "#{name}: #{start_date} thru #{end_date}"
25
28
  end
26
29
 
27
- def previous
28
- if index == 1
29
- (calendar.year_for(start_date) - 1).weeks.last
30
- else
31
- self.class.new(calendar, year_index, index - 1, start_date - 1.week, end_date - 1.week)
32
- end
30
+ # Get the index of this week within its containing year.
31
+ # @return [Integer]
32
+ def index
33
+ @_index ||= (((start_date - year.start_date) + 1) / 7.0).to_i + 1
34
+ end
35
+
36
+ # Get the year number for this week.
37
+ # @return [Integer] (e.g. 2020)
38
+ def year_index
39
+ @_year_index ||= year.year_index
40
+ end
41
+
42
+ private
43
+
44
+ def down
45
+ self.class.new(calendar, start_date - 1.week, end_date - 1.week)
33
46
  end
34
47
 
35
- def next
36
- weeks = calendar.year_for(start_date).weeks
37
- if index == weeks.last.index
38
- self.class.new(calendar, year_index + 1, 1, start_date + 1.week, end_date + 1.week)
39
- else
40
- weeks[index]
41
- end
48
+ def up
49
+ self.class.new(calendar, start_date + 1.week, end_date + 1.week)
42
50
  end
43
51
  end
44
52
  end
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
- require_relative './support/month_based'
2
+ require_relative './support/monthly_unit'
3
3
 
4
4
  module TimeBoss
5
5
  class Calendar
6
- class Year < Support::MonthBased
6
+ class Year < Support::MonthlyUnit
7
7
  NUM_MONTHS = 12
8
8
 
9
+ # Get a simple representation of this year.
10
+ # @return [String] (e.g. "2020")
9
11
  def name
10
12
  year_index.to_s
11
13
  end
12
14
 
13
- def title
14
- name
15
- end
15
+ alias_method :title, :name
16
16
  end
17
17
  end
18
18
  end
@@ -10,12 +10,17 @@ module TimeBoss
10
10
  extend Enumerable
11
11
  delegate :each, :length, to: :all
12
12
 
13
+ # Retrieve a list of all registered calendars.
14
+ # @return [Array<Entry>]
13
15
  def all
14
16
  @_all ||= TimeBoss::Calendar.subclasses.map do |klass|
15
17
  Entry.new(klass.to_s.demodulize.underscore.to_sym, klass)
16
18
  end
17
19
  end
18
20
 
21
+ # Retrieve an instance of the specified named calendar.
22
+ # @param name [String, Symbol] the name of the calendar to retrieve.
23
+ # @return [Calendar]
19
24
  def [](name)
20
25
  find { |e| e.name == name&.to_sym }&.calendar
21
26
  end
@@ -23,6 +28,16 @@ module TimeBoss
23
28
  private
24
29
 
25
30
  Entry = Struct.new(:name, :klass) do
31
+ # @!method name
32
+ # Get the name of the calendar referenced in this entry.
33
+ # @return [Symbol]
34
+
35
+ # @!method klass
36
+ # The class implementing this calendar.
37
+ # @return [Class<Calendar>]
38
+
39
+ # Get an instance of the calendar referenced in this entry.
40
+ # @return [Calendar]
26
41
  def calendar
27
42
  @_calendar ||= klass.new
28
43
  end
@@ -8,6 +8,10 @@ module TimeBoss
8
8
  super(basis: Basis)
9
9
  end
10
10
 
11
+ def supports_weeks?
12
+ true
13
+ end
14
+
11
15
  private
12
16
 
13
17
  class Basis < Calendar::Support::MonthBasis
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ require_relative '../calendar'
3
+
4
+ module TimeBoss
5
+ module Calendars
6
+ class Gregorian < Calendar
7
+ def initialize
8
+ super(basis: Basis)
9
+ end
10
+
11
+ private
12
+
13
+ class Basis < Calendar::Support::MonthBasis
14
+ def start_date
15
+ @_start_date ||= Date.civil(year, month, 1)
16
+ end
17
+
18
+ def end_date
19
+ @_end_date ||= Date.civil(year, month, -1)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module TimeBoss
3
- VERSION = "0.0.8"
3
+ VERSION = "0.2.0"
4
4
  end
@@ -24,7 +24,7 @@ module TimeBoss
24
24
  end
25
25
 
26
26
  describe '#index' do
27
- before(:each) { allow(calendar).to receive(:year_for).with(start_date).and_return double(start_date: start_date - 3) }
27
+ before(:each) { allow(subject).to receive(:year).and_return double(start_date: start_date - 3) }
28
28
 
29
29
  it 'gets its index within the year' do
30
30
  expect(subject.index).to eq 4
@@ -1,15 +1,15 @@
1
1
  module TimeBoss
2
2
  class Calendar
3
3
  module Support
4
- describe MonthBased do
5
- class ChunkMonthBased < described_class
4
+ describe MonthlyUnit do
5
+ class MonthBasedChunk < described_class
6
6
  NUM_MONTHS = 2
7
7
 
8
8
  def name
9
9
  "#{year_index}C#{index}"
10
10
  end
11
11
  end
12
- let(:described_class) { ChunkMonthBased }
12
+ let(:described_class) { MonthBasedChunk }
13
13
  let(:calendar) { double }
14
14
  let(:start_date) { Date.parse('2018-06-25') }
15
15
  let(:end_date) { Date.parse('2018-08-26') }
@@ -30,9 +30,25 @@ module TimeBoss
30
30
  before(:each) { allow(calendar).to receive(:year).with(2018).and_return base }
31
31
 
32
32
  it 'can get the relevant weeks for the period' do
33
+ allow(calendar).to receive(:supports_weeks?).and_return true
33
34
  result = subject.weeks
34
35
  result.each { |w| expect(w).to be_instance_of TimeBoss::Calendar::Week }
35
- expect(result.map(&:name)).to eq %w[2018W26 2018W27 2018W28 2018W29 2018W30 2018W31 2018W32 2018W33 2018W34]
36
+ expect(result.map { |w| w.start_date.to_s }).to eq [
37
+ '2018-06-25',
38
+ '2018-07-02',
39
+ '2018-07-09',
40
+ '2018-07-16',
41
+ '2018-07-23',
42
+ '2018-07-30',
43
+ '2018-08-06',
44
+ '2018-08-13',
45
+ '2018-08-20'
46
+ ]
47
+ end
48
+
49
+ it 'blows up when weeks are not supported' do
50
+ allow(calendar).to receive(:supports_weeks?).and_return false
51
+ expect { subject.weeks }.to raise_error TimeBoss::Calendar::Support::Unit::UnsupportedUnitError
36
52
  end
37
53
  end
38
54
 
@@ -41,24 +57,24 @@ module TimeBoss
41
57
 
42
58
  describe '#previous' do
43
59
  it 'moves easily within itself' do
44
- expect(calendar).to receive(:chunk_month_based).with(48, 3).and_return result
60
+ expect(calendar).to receive(:month_based_chunk).with(48, 3).and_return result
45
61
  expect(described_class.new(calendar, 48, 4, nil, nil).previous).to eq result
46
62
  end
47
63
 
48
64
  it 'flips to the previous container' do
49
- expect(calendar).to receive(:chunk_month_based).with(47, 6).and_return result
65
+ expect(calendar).to receive(:month_based_chunk).with(47, 6).and_return result
50
66
  expect(described_class.new(calendar, 48, 1, nil, nil).previous).to eq result
51
67
  end
52
68
  end
53
69
 
54
70
  describe '#next' do
55
71
  it 'moves easily within itself' do
56
- expect(calendar).to receive(:chunk_month_based).with(48, 3).and_return result
72
+ expect(calendar).to receive(:month_based_chunk).with(48, 3).and_return result
57
73
  expect(described_class.new(calendar, 48, 2, nil, nil).next).to eq result
58
74
  end
59
75
 
60
76
  it 'flips to the previous container' do
61
- expect(calendar).to receive(:chunk_month_based).with(48, 1).and_return result
77
+ expect(calendar).to receive(:month_based_chunk).with(48, 1).and_return result
62
78
  expect(described_class.new(calendar, 47, 6, nil, nil).next).to eq result
63
79
  end
64
80
  end
@@ -1,10 +1,15 @@
1
1
  module TimeBoss
2
2
  class Calendar
3
3
  describe Week do
4
- let(:calendar) { instance_double(TimeBoss::Calendar) }
4
+ let(:calendar) { instance_double(TimeBoss::Calendar, supports_weeks?: true) }
5
5
  let(:start_date) { Date.parse('2048-04-06') }
6
6
  let(:end_date) { Date.parse('2048-04-12') }
7
- let(:subject) { described_class.new(calendar, 2048, 15, start_date, end_date) }
7
+ let(:subject) { described_class.new(calendar, start_date, end_date) }
8
+
9
+ it "doesn't even exist if its calendar doesn't support weeks" do
10
+ allow(calendar).to receive(:supports_weeks?).and_return false
11
+ expect { subject }.to raise_error TimeBoss::Calendar::Support::Unit::UnsupportedUnitError
12
+ end
8
13
 
9
14
  it 'knows its stuff' do
10
15
  expect(subject.start_date).to eq start_date
@@ -12,18 +17,10 @@ module TimeBoss
12
17
  expect(subject.to_range).to eq start_date..end_date
13
18
  end
14
19
 
15
- it 'knows its name' do
16
- expect(subject.name).to eq '2048W15'
17
- end
18
-
19
20
  it 'knows its title' do
20
21
  expect(subject.title).to eq "Week of April 6, 2048"
21
22
  end
22
23
 
23
- it 'can stringify itself' do
24
- expect(subject.to_s).to include(subject.name, start_date.to_s, end_date.to_s)
25
- end
26
-
27
24
  describe '#current?' do
28
25
  it 'knows when it is' do
29
26
  allow(Date).to receive(:today).and_return start_date
@@ -46,13 +43,13 @@ module TimeBoss
46
43
  end
47
44
 
48
45
  it 'can wrap to the previous 52-week year' do
49
- result = described_class.new(calendar, 2022, 1, Date.parse('2021-12-27'), Date.parse('2022-01-02')).previous
46
+ result = described_class.new(calendar, Date.parse('2021-12-27'), Date.parse('2022-01-02')).previous
50
47
  expect(result).to be_a described_class
51
48
  expect(result.to_s).to eq "2021W52: 2021-12-20 thru 2021-12-26"
52
49
  end
53
50
 
54
51
  it 'can wrap to the previous 53-week year' do
55
- result = described_class.new(calendar, 2024, 1, Date.parse('2024-01-01'), Date.parse('2024-01-07')).previous
52
+ result = described_class.new(calendar, Date.parse('2024-01-01'), Date.parse('2024-01-07')).previous
56
53
  expect(result).to be_a described_class
57
54
  expect(result.to_s).to eq "2023W53: 2023-12-25 thru 2023-12-31"
58
55
  end
@@ -66,13 +63,13 @@ module TimeBoss
66
63
  end
67
64
 
68
65
  it 'can wrap from week 52 to the next year' do
69
- result = described_class.new(calendar, 2021, 52, Date.parse('2021-12-20'), Date.parse('2021-12-26')).next
66
+ result = described_class.new(calendar, Date.parse('2021-12-20'), Date.parse('2021-12-26')).next
70
67
  expect(result).to be_a described_class
71
68
  expect(result.to_s).to eq "2022W1: 2021-12-27 thru 2022-01-02"
72
69
  end
73
70
 
74
71
  it 'can wrap from week 53 to the next year' do
75
- result = described_class.new(calendar, 2023, 53, Date.parse('2023-12-25'), Date.parse('2023-12-31')).next
72
+ result = described_class.new(calendar, Date.parse('2023-12-25'), Date.parse('2023-12-31')).next
76
73
  expect(result).to be_a described_class
77
74
  expect(result.to_s).to eq "2024W1: 2024-01-01 thru 2024-01-07"
78
75
  end
@@ -2,6 +2,26 @@ module TimeBoss
2
2
  describe Calendars::Broadcast do
3
3
  let(:subject) { described_class.new }
4
4
 
5
+ context 'days' do
6
+ it 'can get today' do
7
+ day = subject.today
8
+ expect(day).to be_instance_of(TimeBoss::Calendar::Day)
9
+ expect(day.start_date).to eq Date.today
10
+ end
11
+
12
+ it 'can get yesterday' do
13
+ day = subject.yesterday
14
+ expect(day).to be_instance_of(TimeBoss::Calendar::Day)
15
+ expect(day.start_date).to eq Date.yesterday
16
+ end
17
+
18
+ it 'can get tomorrow' do
19
+ day = subject.tomorrow
20
+ expect(day).to be_instance_of(TimeBoss::Calendar::Day)
21
+ expect(day.start_date).to eq Date.tomorrow
22
+ end
23
+ end
24
+
5
25
  context 'quarters' do
6
26
  describe '#quarter' do
7
27
  it 'knows 2017Q2' do
@@ -113,8 +133,8 @@ module TimeBoss
113
133
  expect(quarters.map(&:name)).to eq ['2015Q3', '2015Q4', '2016Q1', '2016Q2', '2016Q3']
114
134
  end
115
135
 
116
- it 'can get a quarter hence' do
117
- quarter = subject.quarters_hence(4)
136
+ it 'can get a quarter ahead' do
137
+ quarter = subject.quarters_ahead(4)
118
138
  expect(quarter).to be_a TimeBoss::Calendar::Quarter
119
139
  expect(quarter.name).to eq '2016Q3'
120
140
  end
@@ -251,8 +271,8 @@ module TimeBoss
251
271
  expect(months.map(&:name)).to eq ['2015M3', '2015M4', '2015M5', '2015M6', '2015M7']
252
272
  end
253
273
 
254
- it 'can get a month hence' do
255
- month = subject.months_hence(4)
274
+ it 'can get a month ahead' do
275
+ month = subject.months_ahead(4)
256
276
  expect(month).to be_a TimeBoss::Calendar::Month
257
277
  expect(month.name).to eq '2015M7'
258
278
  end
@@ -490,7 +510,7 @@ module TimeBoss
490
510
  it 'can parse mathematic expressions' do
491
511
  result = subject.parse('this_month + 2')
492
512
  expect(result).to be_a TimeBoss::Calendar::Month
493
- expect(result).to eq subject.months_hence(2)
513
+ expect(result).to eq subject.months_ahead(2)
494
514
  end
495
515
 
496
516
  context 'ranges' do
@@ -554,7 +574,7 @@ module TimeBoss
554
574
 
555
575
  it 'can shift to a different year' do
556
576
  allow(subject).to receive(:this_year).and_return subject.parse('2019')
557
- result = basis.years_hence(3)
577
+ result = basis.years_ahead(3)
558
578
  expect(result).to be_a TimeBoss::Calendar::Day
559
579
  expect(result.to_s).to eq '2022-04-19'
560
580
  expect(basis.in_year).to eq 114
@@ -596,7 +616,7 @@ module TimeBoss
596
616
 
597
617
  it 'can shift to a different year' do
598
618
  allow(subject).to receive(:this_year).and_return subject.parse('2020')
599
- result = basis.years_hence(4)
619
+ result = basis.years_ahead(4)
600
620
  expect(result).to be_a TimeBoss::Calendar::Month
601
621
  expect(result.name).to eq '2024M4'
602
622
  expect(basis.in_year).to eq 4
@@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "rack-test"
28
28
  spec.add_development_dependency "rake"
29
29
  spec.add_development_dependency "rspec"
30
+ spec.add_development_dependency "yard"
30
31
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timeboss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin McDonald
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-17 00:00:00.000000000 Z
11
+ date: 2020-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: yard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  description: Broadcast Calendar navigation in Ruby made simple
112
126
  email:
113
127
  - kevinstuffandthings@gmail.com
@@ -116,9 +130,13 @@ executables:
116
130
  extensions: []
117
131
  extra_rdoc_files: []
118
132
  files:
133
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
134
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
119
135
  - ".gitignore"
120
136
  - ".rspec"
121
137
  - ".travis.yml"
138
+ - ".yardopts"
139
+ - CODE_OF_CONDUCT.md
122
140
  - Gemfile
123
141
  - LICENSE.txt
124
142
  - README.md
@@ -135,20 +153,25 @@ files:
135
153
  - lib/timeboss/calendar/period.rb
136
154
  - lib/timeboss/calendar/quarter.rb
137
155
  - lib/timeboss/calendar/support/formatter.rb
138
- - lib/timeboss/calendar/support/month_based.rb
139
156
  - lib/timeboss/calendar/support/month_basis.rb
157
+ - lib/timeboss/calendar/support/monthly_unit.rb
158
+ - lib/timeboss/calendar/support/navigable.rb
140
159
  - lib/timeboss/calendar/support/shiftable.rb
160
+ - lib/timeboss/calendar/support/translatable.rb
141
161
  - lib/timeboss/calendar/support/unit.rb
142
162
  - lib/timeboss/calendar/waypoints.rb
163
+ - lib/timeboss/calendar/waypoints/absolute.rb
164
+ - lib/timeboss/calendar/waypoints/relative.rb
143
165
  - lib/timeboss/calendar/week.rb
144
166
  - lib/timeboss/calendar/year.rb
145
167
  - lib/timeboss/calendars.rb
146
168
  - lib/timeboss/calendars/broadcast.rb
169
+ - lib/timeboss/calendars/gregorian.rb
147
170
  - lib/timeboss/support/shellable.rb
148
171
  - lib/timeboss/version.rb
149
172
  - spec/calendar/day_spec.rb
150
173
  - spec/calendar/quarter_spec.rb
151
- - spec/calendar/support/month_based_spec.rb
174
+ - spec/calendar/support/monthly_unit_spec.rb
152
175
  - spec/calendar/support/unit_spec.rb
153
176
  - spec/calendar/week_spec.rb
154
177
  - spec/calendars/broadcast_spec.rb
@@ -182,7 +205,7 @@ summary: Broadcast Calendar navigation in Ruby made simple
182
205
  test_files:
183
206
  - spec/calendar/day_spec.rb
184
207
  - spec/calendar/quarter_spec.rb
185
- - spec/calendar/support/month_based_spec.rb
208
+ - spec/calendar/support/monthly_unit_spec.rb
186
209
  - spec/calendar/support/unit_spec.rb
187
210
  - spec/calendar/week_spec.rb
188
211
  - spec/calendars/broadcast_spec.rb