timeboss 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  4. data/.gitignore +5 -0
  5. data/.replit +2 -0
  6. data/.rspec +2 -0
  7. data/.travis.yml +16 -0
  8. data/.yardopts +1 -0
  9. data/CODE_OF_CONDUCT.md +76 -0
  10. data/Gemfile +3 -0
  11. data/LICENSE.txt +22 -0
  12. data/README.md +233 -0
  13. data/Rakefile +5 -0
  14. data/bin/tbsh +15 -0
  15. data/lib/tasks/calendars.rake +22 -0
  16. data/lib/tasks/timeboss.rake +6 -0
  17. data/lib/timeboss.rb +6 -0
  18. data/lib/timeboss/calendar.rb +64 -0
  19. data/lib/timeboss/calendar/day.rb +48 -0
  20. data/lib/timeboss/calendar/half.rb +22 -0
  21. data/lib/timeboss/calendar/month.rb +22 -0
  22. data/lib/timeboss/calendar/parser.rb +53 -0
  23. data/lib/timeboss/calendar/period.rb +83 -0
  24. data/lib/timeboss/calendar/quarter.rb +22 -0
  25. data/lib/timeboss/calendar/support/formatter.rb +33 -0
  26. data/lib/timeboss/calendar/support/month_basis.rb +21 -0
  27. data/lib/timeboss/calendar/support/monthly_unit.rb +55 -0
  28. data/lib/timeboss/calendar/support/navigable.rb +72 -0
  29. data/lib/timeboss/calendar/support/shiftable.rb +241 -0
  30. data/lib/timeboss/calendar/support/translatable.rb +93 -0
  31. data/lib/timeboss/calendar/support/unit.rb +88 -0
  32. data/lib/timeboss/calendar/waypoints.rb +12 -0
  33. data/lib/timeboss/calendar/waypoints/absolute.rb +113 -0
  34. data/lib/timeboss/calendar/waypoints/relative.rb +267 -0
  35. data/lib/timeboss/calendar/week.rb +53 -0
  36. data/lib/timeboss/calendar/year.rb +18 -0
  37. data/lib/timeboss/calendars.rb +53 -0
  38. data/lib/timeboss/calendars/broadcast.rb +32 -0
  39. data/lib/timeboss/calendars/gregorian.rb +30 -0
  40. data/lib/timeboss/support/shellable.rb +17 -0
  41. data/lib/timeboss/version.rb +4 -0
  42. data/spec/calendar/day_spec.rb +60 -0
  43. data/spec/calendar/quarter_spec.rb +32 -0
  44. data/spec/calendar/support/monthly_unit_spec.rb +85 -0
  45. data/spec/calendar/support/unit_spec.rb +90 -0
  46. data/spec/calendar/week_spec.rb +80 -0
  47. data/spec/calendars/broadcast_spec.rb +796 -0
  48. data/spec/calendars/gregorian_spec.rb +684 -0
  49. data/spec/calendars_spec.rb +50 -0
  50. data/spec/spec_helper.rb +12 -0
  51. data/timeboss.gemspec +31 -0
  52. metadata +216 -0
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+ module TimeBoss
3
+ class Calendar
4
+ module Support
5
+ module Navigable
6
+ # @overload previous
7
+ # Fetch the previous unit relative to this unit.
8
+ # @return [Unit]
9
+ # @overload previous(value)
10
+ # Fetch some previous number of units relative to this unit
11
+ # @param quantity [Integer]
12
+ # @return [Array<Unit>]
13
+ def previous(quantity = nil)
14
+ return down if quantity.nil?
15
+ gather(:previous, quantity).reverse
16
+ end
17
+
18
+ # @overload next
19
+ # Fetch the next unit relative to this unit.
20
+ # @return [Unit]
21
+ # @overload next(value)
22
+ # Fetch some next number of units relative to this unit
23
+ # @param quantity [Integer]
24
+ # @return [Array<Unit>]
25
+ def next(quantity = nil)
26
+ return up if quantity.nil?
27
+ gather(:next, quantity)
28
+ end
29
+
30
+ # Fetch the unit some number of units prior to this unit.
31
+ # @param quantity [Integer]
32
+ # @return [Unit]
33
+ def ago(quantity)
34
+ previous(quantity + 1).first
35
+ end
36
+
37
+ # Fetch the unit some number of units after this unit.
38
+ # @param quantity [Integer]
39
+ # @return [Unit]
40
+ def ahead(quantity)
41
+ self.next(quantity + 1).last
42
+ end
43
+
44
+ # Fetch a list of units from this unit until some date.
45
+ # @param end_date [Date]
46
+ # @return [Array<Unit>]
47
+ def until(end_date)
48
+ entry = self
49
+ [entry].tap do |entries|
50
+ until entry.end_date >= end_date
51
+ entry = entry.next
52
+ entries << entry
53
+ end
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def gather(navigator, quantity)
60
+ [].tap do |entries|
61
+ entry = self
62
+ while quantity > 0
63
+ entries << entry
64
+ entry = entry.send(navigator)
65
+ quantity -= 1
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+ module TimeBoss
3
+ class Calendar
4
+ module Support
5
+ module Shiftable
6
+ Support::Translatable::PERIODS.each do |period|
7
+ periods = period.pluralize
8
+
9
+ define_method("in_#{period}") do
10
+ base = send(periods)
11
+ return unless base.length == 1
12
+ base.first.send(self.class.type.to_s.pluralize).find_index { |p| p == self } + 1
13
+ end
14
+
15
+ define_method("#{periods}_ago") do |offset|
16
+ base_offset = send("in_#{period}") or return
17
+ (calendar.send("this_#{period}") - offset).send(self.class.type.to_s.pluralize)[base_offset - 1]
18
+ end
19
+
20
+ define_method("#{periods}_ahead") { |o| send("#{periods}_ago", o * -1) }
21
+
22
+ define_method("last_#{period}") { send("#{periods}_ago", 1) }
23
+ define_method("this_#{period}") { send("#{periods}_ago", 0) }
24
+ define_method("next_#{period}") { send("#{periods}_ahead", 1) }
25
+ end
26
+
27
+ alias_method :yesterday, :last_day
28
+ alias_method :today, :this_day
29
+ alias_method :tomorrow, :next_day
30
+
31
+ #
32
+ # i hate this
33
+ #
34
+
35
+ ### Days
36
+
37
+ # @!method in_day
38
+ # Get the index within the day that this unit falls in.
39
+ # Returns nil if no single day can be identified.
40
+ # @return [Integer, nil]
41
+
42
+ # @!method days_ago
43
+ # Get the index-relative day some number of days ago.
44
+ # Returns nil if no single day can be identified.
45
+ # @param offset [Integer] the number of days back to shift this period
46
+ # @return [Calendar::Day, nil]
47
+
48
+ # @!method days_ahead
49
+ # Get the index-relative day some number of days ahead.
50
+ # Returns nil if no single day can be identified.
51
+ # @param offset [Integer] the number of days forward to shift this period
52
+ # @return [Calendar::Day, nil]
53
+
54
+ # @!method last_day
55
+ # Get the index-relative day 1 day ago.
56
+ # Returns nil if no single day can be identified.
57
+ # @return [Calendar::Day, nil]
58
+
59
+ # @!method this_day
60
+ # Get the index-relative day for this day.
61
+ # Returns nil if no single day can be identified.
62
+ # @return [Calendar::Day, nil]
63
+
64
+ # @!method next_day
65
+ # Get the index-relative day 1 day forward.
66
+ # Returns nil if no single day can be identified.
67
+ # @return [Calendar::Day, nil]
68
+
69
+ ### Weeks
70
+
71
+ # @!method in_week
72
+ # Get the index within the week that this unit falls in.
73
+ # Returns nil if no single week can be identified.
74
+ # @return [Integer, nil]
75
+
76
+ # @!method weeks_ago
77
+ # Get the index-relative week some number of weeks ago.
78
+ # Returns nil if no single week can be identified.
79
+ # @param offset [Integer] the number of weeks back to shift this period
80
+ # @return [Calendar::Week, nil]
81
+
82
+ # @!method weeks_ahead
83
+ # Get the index-relative week some number of weeks ahead.
84
+ # Returns nil if no single week can be identified.
85
+ # @param offset [Integer] the number of weeks forward to shift this period
86
+ # @return [Calendar::Week, nil]
87
+
88
+ # @!method last_week
89
+ # Get the index-relative week 1 week ago.
90
+ # Returns nil if no single week can be identified.
91
+ # @return [Calendar::Week, nil]
92
+
93
+ # @!method this_week
94
+ # Get the index-relative week for this week.
95
+ # Returns nil if no single week can be identified.
96
+ # @return [Calendar::Week, nil]
97
+
98
+ # @!method next_week
99
+ # Get the index-relative week 1 week forward.
100
+ # Returns nil if no single week can be identified.
101
+ # @return [Calendar::Week, nil]
102
+
103
+ ### Months
104
+
105
+ # @!method in_month
106
+ # Get the index within the month that this unit falls in.
107
+ # Returns nil if no single month can be identified.
108
+ # @return [Integer, nil]
109
+
110
+ # @!method months_ago
111
+ # Get the index-relative month some number of months ago.
112
+ # Returns nil if no single month can be identified.
113
+ # @param offset [Integer] the number of months back to shift this period
114
+ # @return [Calendar::Month, nil]
115
+
116
+ # @!method months_ahead
117
+ # Get the index-relative month some number of months ahead.
118
+ # Returns nil if no single month can be identified.
119
+ # @param offset [Integer] the number of months forward to shift this period
120
+ # @return [Calendar::Month, nil]
121
+
122
+ # @!method last_month
123
+ # Get the index-relative month 1 month ago.
124
+ # Returns nil if no single month can be identified.
125
+ # @return [Calendar::Month, nil]
126
+
127
+ # @!method this_month
128
+ # Get the index-relative month for this month.
129
+ # Returns nil if no single month can be identified.
130
+ # @return [Calendar::Month, nil]
131
+
132
+ # @!method next_month
133
+ # Get the index-relative month 1 month forward.
134
+ # Returns nil if no single month can be identified.
135
+ # @return [Calendar::Month, nil]
136
+
137
+ ### Quarters
138
+
139
+ # @!method in_quarter
140
+ # Get the index within the quarter that this unit falls in.
141
+ # Returns nil if no single quarter can be identified.
142
+ # @return [Integer, nil]
143
+
144
+ # @!method quarters_ago
145
+ # Get the index-relative quarter some number of quarters ago.
146
+ # Returns nil if no single quarter can be identified.
147
+ # @param offset [Integer] the number of quarters back to shift this period
148
+ # @return [Calendar::Quarter, nil]
149
+
150
+ # @!method quarters_ahead
151
+ # Get the index-relative quarter some number of quarters ahead.
152
+ # Returns nil if no single quarter can be identified.
153
+ # @param offset [Integer] the number of quarters forward to shift this period
154
+ # @return [Calendar::Quarter, nil]
155
+
156
+ # @!method last_quarter
157
+ # Get the index-relative quarter 1 quarter ago.
158
+ # Returns nil if no single quarter can be identified.
159
+ # @return [Calendar::Quarter, nil]
160
+
161
+ # @!method this_quarter
162
+ # Get the index-relative quarter for this quarter.
163
+ # Returns nil if no single quarter can be identified.
164
+ # @return [Calendar::Quarter, nil]
165
+
166
+ # @!method next_quarter
167
+ # Get the index-relative quarter 1 quarter forward.
168
+ # Returns nil if no single quarter can be identified.
169
+ # @return [Calendar::Quarter, nil]
170
+
171
+ ### Halves
172
+
173
+ # @!method in_half
174
+ # Get the index within the half that this unit falls in.
175
+ # Returns nil if no single half can be identified.
176
+ # @return [Integer, nil]
177
+
178
+ # @!method halves_ago
179
+ # Get the index-relative half some number of halves ago.
180
+ # Returns nil if no single half can be identified.
181
+ # @param offset [Integer] the number of halves back to shift this period
182
+ # @return [Calendar::Half, nil]
183
+
184
+ # @!method halves_ahead
185
+ # Get the index-relative half some number of halves ahead.
186
+ # Returns nil if no single half can be identified.
187
+ # @param offset [Integer] the number of halves forward to shift this period
188
+ # @return [Calendar::Half, nil]
189
+
190
+ # @!method last_half
191
+ # Get the index-relative half 1 half ago.
192
+ # Returns nil if no single half can be identified.
193
+ # @return [Calendar::Half, nil]
194
+
195
+ # @!method this_half
196
+ # Get the index-relative half for this half.
197
+ # Returns nil if no single half can be identified.
198
+ # @return [Calendar::Half, nil]
199
+
200
+ # @!method next_half
201
+ # Get the index-relative half 1 half forward.
202
+ # Returns nil if no single half can be identified.
203
+ # @return [Calendar::Half, nil]
204
+
205
+ ### Years
206
+
207
+ # @!method in_year
208
+ # Get the index within the year that this unit falls in.
209
+ # Returns nil if no single year can be identified.
210
+ # @return [Integer, nil]
211
+
212
+ # @!method years_ago
213
+ # Get the index-relative year some number of years ago.
214
+ # Returns nil if no single year can be identified.
215
+ # @param offset [Integer] the number of years back to shift this period
216
+ # @return [Calendar::Year, nil]
217
+
218
+ # @!method years_ahead
219
+ # Get the index-relative year some number of years ahead.
220
+ # Returns nil if no single year can be identified.
221
+ # @param offset [Integer] the number of years forward to shift this period
222
+ # @return [Calendar::Year, nil]
223
+
224
+ # @!method last_year
225
+ # Get the index-relative year 1 year ago.
226
+ # Returns nil if no single year can be identified.
227
+ # @return [Calendar::Year, nil]
228
+
229
+ # @!method this_year
230
+ # Get the index-relative year for this year.
231
+ # Returns nil if no single year can be identified.
232
+ # @return [Calendar::Year, nil]
233
+
234
+ # @!method next_year
235
+ # Get the index-relative year 1 year forward.
236
+ # Returns nil if no single year can be identified.
237
+ # @return [Calendar::Year, nil]
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+ module TimeBoss
3
+ class Calendar
4
+ module Support
5
+ module Translatable
6
+ PERIODS = %w[day week month quarter half year]
7
+
8
+ PERIODS.each do |period|
9
+ periods = period.pluralize
10
+
11
+ define_method(periods) { calendar.send("#{periods}_for", self) }
12
+
13
+ define_method(period) do |index = nil|
14
+ entries = send(periods)
15
+ return entries[index - 1] unless index.nil?
16
+ return nil unless entries.length == 1
17
+ entries.first
18
+ end
19
+ end
20
+
21
+ #
22
+ # i hate this
23
+ #
24
+
25
+ ### Days
26
+
27
+ # @!method days
28
+ # Get a list of days that fall within this unit.
29
+ # @return [Array<Calendar::Day>]
30
+
31
+ # @!method day(index = nil)
32
+ # Get the day this unit represents.
33
+ # Returns nil if no single day can be identified.
34
+ # @return [Array<Calendar::Day>, nil]
35
+
36
+ ### Weeks
37
+
38
+ # @!method weeks
39
+ # Get a list of weeks that fall within this unit.
40
+ # @return [Array<Calendar::Week>]
41
+
42
+ # @!method week(index = nil)
43
+ # Get the week this unit represents.
44
+ # Returns nil if no single week can be identified.
45
+ # @return [Array<Calendar::Week>, nil]
46
+
47
+ ### Months
48
+
49
+ # @!method months
50
+ # Get a list of months that fall within this unit.
51
+ # @return [Array<Calendar::Month>]
52
+
53
+ # @!method month(index = nil)
54
+ # Get the month this unit represents.
55
+ # Returns nil if no single month can be identified.
56
+ # @return [Array<Calendar::Month>, nil]
57
+
58
+ ### Quarters
59
+
60
+ # @!method quarters
61
+ # Get a list of quarters that fall within this unit.
62
+ # @return [Array<Calendar::Quarter>]
63
+
64
+ # @!method quarter(index = nil)
65
+ # Get the quarter this unit represents.
66
+ # Returns nil if no single quarter can be identified.
67
+ # @return [Array<Calendar::Quarter>, nil]
68
+
69
+ ### Halves
70
+
71
+ # @!method halves
72
+ # Get a list of halves that fall within this unit.
73
+ # @return [Array<Calendar::Half>]
74
+
75
+ # @!method half(index = nil)
76
+ # Get the half this unit represents.
77
+ # Returns nil if no single half can be identified.
78
+ # @return [Array<Calendar::Half>, nil]
79
+
80
+ ### Years
81
+
82
+ # @!method years
83
+ # Get a list of years that fall within this unit.
84
+ # @return [Array<Calendar::Year>]
85
+
86
+ # @!method year(index = nil)
87
+ # Get the year this unit represents.
88
+ # Returns nil if no single year can be identified.
89
+ # @return [Array<Calendar::Year>, nil]
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+ require_relative './navigable'
3
+ require_relative './translatable'
4
+ require_relative './shiftable'
5
+ require_relative './formatter'
6
+
7
+ module TimeBoss
8
+ class Calendar
9
+ module Support
10
+ class Unit
11
+ include Navigable
12
+ include Translatable
13
+ include Shiftable
14
+ attr_reader :calendar, :start_date, :end_date
15
+
16
+ UnsupportedUnitError = Class.new(StandardError)
17
+
18
+ def self.type
19
+ self.name.demodulize.underscore
20
+ end
21
+
22
+ def initialize(calendar, start_date, end_date)
23
+ @calendar = calendar
24
+ @start_date = start_date
25
+ @end_date = end_date
26
+ end
27
+
28
+ # Is the specified unit equal to this one, based on its unit type and date range?
29
+ # @param entry [Unit] the unit to compare
30
+ # @return [Boolean] true when periods are equal
31
+ def ==(entry)
32
+ self.class == entry.class && self.start_date == entry.start_date && self.end_date == entry.end_date
33
+ end
34
+
35
+ # Format this period based on specified granularities.
36
+ # @param periods [Array<Symbol, String>] the periods to include (`half, week`, or `quarter`)
37
+ # @return [String] (e.g. "2020H2W7" or "2020Q3")
38
+ def format(*periods)
39
+ Formatter.new(self, periods.presence || Formatter::PERIODS).to_s
40
+ end
41
+
42
+ # Starting from this unit of time, build a period extending through the specified time unit.
43
+ # @param unit [Unit] the period to extend through
44
+ # @return [Period]
45
+ def thru(unit)
46
+ Period.new(calendar, self, unit)
47
+ end
48
+
49
+ # Does this period cover the current date?
50
+ # @return [Boolean]
51
+ def current?
52
+ Date.today.between?(start_date, end_date)
53
+ end
54
+
55
+ # Return the unit relative to this one by the specified offset.
56
+ # Offset values can be positive or negative.
57
+ # @param value [Integer]
58
+ # @return [Unit]
59
+ def offset(value)
60
+ method = value.negative? ? :previous : :next
61
+ base = self
62
+ value.abs.times { base = base.send(method) }
63
+ base
64
+ end
65
+
66
+ # Move some number of units forward from this unit.
67
+ # @param value [Integer]
68
+ # @return [Unit]
69
+ def +(value)
70
+ offset(value)
71
+ end
72
+
73
+ # Move some number of units backward from this unit.
74
+ # @param value [Integer]
75
+ # @return [Unit]
76
+ def -(value)
77
+ offset(-value)
78
+ end
79
+
80
+ # Express this period as a date range.
81
+ # @return [Range<Date, Date>]
82
+ def to_range
83
+ @_to_range ||= start_date .. end_date
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end