timeboss 0.0.8 → 0.2.0

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