timeboss 0.2.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a8966731f645860d665b2a333b2996dad5e1224d25703ddfdd4f155779645df
4
- data.tar.gz: d087b039000b8b7999eaeae1ba98526618741aef3535238551314e79446d9b8d
3
+ metadata.gz: b0d4b9c100b0d59e5aa5b71152c390f59deedce0e071c0d5c82666dba445946d
4
+ data.tar.gz: dc533dd7884b186571322d2229823f47da014a623a1f50535a7c723f4d6c355c
5
5
  SHA512:
6
- metadata.gz: 31c77aac0422e2ba3da7c8422014ddfa0196388c27c4d9e78775909099a6a2461d0b6d86f5f2b265fda05718711e60818f33519c17f025f4952cf7ff950444e2
7
- data.tar.gz: a5cbb8b93686382b75222a191405de36449d725dee28db2dd396c9d8e394fa41d8da7dd41229c3c9674d7ba6c2ad78f9fe03f720a3279d47f0f4cc14eb41d939
6
+ metadata.gz: 0acc27cc7254a3e0d67df651ddf2462bb606e2e844e7076bf36fd9c5ce0eacb2ccf08963ee418f41204c60c6acd6d0783fed60871b4729b46870c6f133de28f3
7
+ data.tar.gz: '0281773958cbad85a0b10997c3ed058797906a5fed2f5452a572922492cabf27021871ea018aec5e8b85e078803a750a7f004f1d2b04ec1d6518002c0552360a'
data/.replit CHANGED
@@ -1,2 +1,2 @@
1
1
  language = "ruby"
2
- run = "rake timeboss:calendars:broadcast:repl"
2
+ run = "bundle exec rake timeboss:calendars:broadcast:repl"
data/README.md CHANGED
@@ -40,7 +40,7 @@ You can ask simple questions of the calendar:
40
40
 
41
41
  ```ruby
42
42
  period = calendar.parse('2019Q4') # or '2018', or '2019-12-21', or '2020W32', or '2020M3W2'
43
- # => #<TimeBoss::Calendar::Quarter:0x007f82d50e2478>
43
+ # => #<TimeBoss::Calendar::Quarter start_date=2019-09-30, end_date=2019-12-29>
44
44
  period.to_s
45
45
  # => "2019Q4: 2019-09-30 thru 2019-12-29"
46
46
  period.next.start_date.to_s # try previous, too!
@@ -117,6 +117,9 @@ week = calendar.parse('2014W29').this_week
117
117
 
118
118
  calendar.this_week.next_year.to_s # run 2020W29
119
119
  # => "2021W29: 2021-07-12 thru 2021-07-18"
120
+
121
+ calendar.week(2016, this_week.in_year) # run 2020-07-22
122
+ # => #<TimeBoss::Calendar::Week start_date=2016-07-18, end_date=2016-07-24>
120
123
  ```
121
124
 
122
125
  Complicated range expressions can be parsed using the `..` range operator, or evaluated with `thru`:
@@ -147,9 +150,9 @@ You will find yourself in the context of an instantiated `TimeBoss::Calendar` ob
147
150
  ```bash
148
151
  $ tbsh
149
152
  2.4.1 :001 > next_quarter
150
- => #<TimeBoss::Calendar::Quarter:0x007fe04c16a1c8 @calendar=#<TimeBoss::Calendars::Broadcast:0x007fe04c1a0458 @basis=TimeBoss::Calendars::Broadcast::Basis>, @year_index=2020, @index=4, @start_date=#<Date: 2020-09-28 ((2459121j,0s,0n),+0s,2299161j)>, @end_date=#<Date: 2020-12-27 ((2459211j,0s,0n),+0s,2299161j)>>
153
+ => #<TimeBoss::Calendar::Quarter start_date=2020-09-28, end_date=2020-12-27>
151
154
  2.4.1 :002 > last_year
152
- => #<TimeBoss::Calendar::Year:0x007fe04c161ca8 @calendar=#<TimeBoss::Calendars::Broadcast:0x007fe04c1a0458 @basis=TimeBoss::Calendars::Broadcast::Basis>, @year_index=2019, @index=1, @start_date=#<Date: 2018-12-31 ((2458484j,0s,0n),+0s,2299161j)>, @end_date=#<Date: 2019-12-29 ((2458847j,0s,0n),+0s,2299161j)>>
155
+ => #<TimeBoss::Calendar::Year start_date=2018-12-31, end_date=2019-12-29>
153
156
  2.4.1 :003 > parse('this_quarter .. this_quarter+4').months.map(&:name)
154
157
  => ["2020M7", "2020M8", "2020M9", "2020M10", "2020M11", "2020M12", "2021M1", "2021M2", "2021M3", "2021M4", "2021M5", "2021M6", "2021M7", "2021M8", "2021M9"]
155
158
  ```
@@ -221,9 +224,10 @@ With the new calendar implemented, it can be accessed in one of 2 ways:
221
224
 
222
225
  ```ruby
223
226
  require 'timeboss/calendars'
227
+ TimeBoss::Calendars.register(:august_fiscal, MyCalendars::AugustFiscal)
228
+
229
+ # You'll get a cached instance of your calendar here.
230
+ # Handy when switching back and forth between different calendar implementations.
224
231
  calendar = TimeBoss::Calendars[:august_fiscal]
225
232
  calendar.this_year
226
233
  ```
227
-
228
- ## TODO
229
- - [ ] Add comprehensive documentation
@@ -41,6 +41,12 @@ module TimeBoss
41
41
  true
42
42
  end
43
43
 
44
+ def self.register!
45
+ return unless TimeBoss::Calendars.method_defined?(:register)
46
+ TimeBoss::Calendars.register(self.name.to_s.demodulize.underscore, self)
47
+ end
48
+ private_class_method :register!
49
+
44
50
  protected
45
51
 
46
52
  attr_reader :basis
@@ -11,7 +11,8 @@ module TimeBoss
11
11
  end
12
12
 
13
13
  def parse(identifier = nil)
14
- return parse_identifier(identifier.presence) unless identifier&.include?(RANGE_DELIMITER)
14
+ return nil unless (identifier || '').strip.length > 0
15
+ return parse_identifier(identifier) unless identifier&.include?(RANGE_DELIMITER)
15
16
  bases = identifier.split(RANGE_DELIMITER).map { |i| parse_identifier(i.strip) } unless identifier.nil?
16
17
  bases ||= [parse_identifier(nil)]
17
18
  Period.new(calendar, *bases)
@@ -34,31 +34,106 @@ module TimeBoss
34
34
  end
35
35
  end
36
36
 
37
+ #
38
+ # i hate this
39
+ #
40
+
41
+ ### Days
42
+
43
+ # @!method days
44
+ # Get a list of days that fall within this period.
45
+ # @return [Array<Calendar::Day>]
46
+
47
+ # @!method day(index = nil)
48
+ # Get the day this period represents.
49
+ # Returns nil if no single day can be identified.
50
+ # @return [Array<Calendar::Day>, nil]
51
+
52
+ ### Weeks
53
+
54
+ # @!method weeks
55
+ # Get a list of weeks that fall within this period.
56
+ # @return [Array<Calendar::Week>]
57
+
58
+ # @!method week(index = nil)
59
+ # Get the week this period represents.
60
+ # Returns nil if no single week can be identified.
61
+ # @return [Array<Calendar::Week>, nil]
62
+
63
+ ### Months
64
+
65
+ # @!method months
66
+ # Get a list of months that fall within this period.
67
+ # @return [Array<Calendar::Month>]
68
+
69
+ # @!method month(index = nil)
70
+ # Get the month this period represents.
71
+ # Returns nil if no single month can be identified.
72
+ # @return [Array<Calendar::Month>, nil]
73
+
74
+ ### Quarters
75
+
76
+ # @!method quarters
77
+ # Get a list of quarters that fall within this period.
78
+ # @return [Array<Calendar::Quarter>]
79
+
80
+ # @!method quarter(index = nil)
81
+ # Get the quarter this period represents.
82
+ # Returns nil if no single quarter can be identified.
83
+ # @return [Array<Calendar::Quarter>, nil]
84
+
85
+ ### Halves
86
+
87
+ # @!method halves
88
+ # Get a list of halves that fall within this period.
89
+ # @return [Array<Calendar::Half>]
90
+
91
+ # @!method half(index = nil)
92
+ # Get the half this period represents.
93
+ # Returns nil if no single half can be identified.
94
+ # @return [Array<Calendar::Half>, nil]
95
+
96
+ ### Years
97
+
98
+ # @!method years
99
+ # Get a list of years that fall within this period.
100
+ # @return [Array<Calendar::Year>]
101
+
102
+ # @!method year(index = nil)
103
+ # Get the year this period represents.
104
+ # Returns nil if no single year can be identified.
105
+ # @return [Array<Calendar::Year>, nil]
106
+
107
+ # Does this period cover the current date?
108
+ # @return [Boolean]
109
+ def current?
110
+ to_range.include?(Date.today)
111
+ end
112
+
37
113
  %w[day week month quarter half year].each do |size|
38
114
  define_method(size.pluralize) do
39
115
  entry = calendar.send("#{size}_for", self.begin.start_date)
40
116
  build_entries entry
41
117
  end
42
118
 
43
- define_method(size) do
119
+ define_method(size) do |index = nil|
44
120
  entries = send(size.pluralize)
121
+ return entries[index - 1] unless index.nil?
45
122
  return nil unless entries.length == 1
46
123
  entries.first
47
124
  end
48
125
  end
49
126
 
50
- # Does this period cover the current date?
51
- # @return [Boolean]
52
- def current?
53
- to_range.include?(Date.today)
54
- end
55
-
56
127
  # Express this period as a date range.
57
128
  # @return [Range<Date, Date>]
58
129
  def to_range
59
130
  @_to_range ||= start_date .. end_date
60
131
  end
61
132
 
133
+ def inspect
134
+ "#<#{self.class.name}[#{self.begin.inspect}..#{self.end.inspect}] start_date=#{start_date}, end_date=#{end_date}>"
135
+ end
136
+
62
137
  private
63
138
 
64
139
  attr_reader :calendar
@@ -2,6 +2,8 @@ module TimeBoss
2
2
  class Calendar
3
3
  module Support
4
4
  # @abstract
5
+ # A MonthBasis must define a `#start_date` and `#end_date` method.
6
+ # These methods should be calculated based on the incoming `#year` and `#month` values.
5
7
  class MonthBasis
6
8
  attr_reader :year, :month
7
9
 
@@ -10,8 +10,9 @@ module TimeBoss
10
10
 
11
11
  define_method(periods) { calendar.send("#{periods}_for", self) }
12
12
 
13
- define_method(period) do
13
+ define_method(period) do |index = nil|
14
14
  entries = send(periods)
15
+ return entries[index - 1] unless index.nil?
15
16
  return nil unless entries.length == 1
16
17
  entries.first
17
18
  end
@@ -27,7 +28,7 @@ module TimeBoss
27
28
  # Get a list of days that fall within this unit.
28
29
  # @return [Array<Calendar::Day>]
29
30
 
30
- # @!method day
31
+ # @!method day(index = nil)
31
32
  # Get the day this unit represents.
32
33
  # Returns nil if no single day can be identified.
33
34
  # @return [Array<Calendar::Day>, nil]
@@ -38,7 +39,7 @@ module TimeBoss
38
39
  # Get a list of weeks that fall within this unit.
39
40
  # @return [Array<Calendar::Week>]
40
41
 
41
- # @!method week
42
+ # @!method week(index = nil)
42
43
  # Get the week this unit represents.
43
44
  # Returns nil if no single week can be identified.
44
45
  # @return [Array<Calendar::Week>, nil]
@@ -49,7 +50,7 @@ module TimeBoss
49
50
  # Get a list of months that fall within this unit.
50
51
  # @return [Array<Calendar::Month>]
51
52
 
52
- # @!method month
53
+ # @!method month(index = nil)
53
54
  # Get the month this unit represents.
54
55
  # Returns nil if no single month can be identified.
55
56
  # @return [Array<Calendar::Month>, nil]
@@ -60,7 +61,7 @@ module TimeBoss
60
61
  # Get a list of quarters that fall within this unit.
61
62
  # @return [Array<Calendar::Quarter>]
62
63
 
63
- # @!method quarter
64
+ # @!method quarter(index = nil)
64
65
  # Get the quarter this unit represents.
65
66
  # Returns nil if no single quarter can be identified.
66
67
  # @return [Array<Calendar::Quarter>, nil]
@@ -71,7 +72,7 @@ module TimeBoss
71
72
  # Get a list of halves that fall within this unit.
72
73
  # @return [Array<Calendar::Half>]
73
74
 
74
- # @!method half
75
+ # @!method half(index = nil)
75
76
  # Get the half this unit represents.
76
77
  # Returns nil if no single half can be identified.
77
78
  # @return [Array<Calendar::Half>, nil]
@@ -82,7 +83,7 @@ module TimeBoss
82
83
  # Get a list of years that fall within this unit.
83
84
  # @return [Array<Calendar::Year>]
84
85
 
85
- # @!method year
86
+ # @!method year(index = nil)
86
87
  # Get the year this unit represents.
87
88
  # Returns nil if no single year can be identified.
88
89
  # @return [Array<Calendar::Year>, nil]
@@ -82,6 +82,10 @@ module TimeBoss
82
82
  def to_range
83
83
  @_to_range ||= start_date .. end_date
84
84
  end
85
+
86
+ def inspect
87
+ "#<#{self.class.name} start_date=#{start_date}, end_date=#{end_date}>"
88
+ end
85
89
  end
86
90
  end
87
91
  end
@@ -1,28 +1,33 @@
1
1
  # frozen_string_literal: true
2
- require 'active_support/core_ext/class/subclasses'
3
2
  require_relative 'calendar'
4
3
 
5
- Dir[File.expand_path('../calendars/*.rb', __FILE__)].each { |f| require f }
6
-
7
4
  module TimeBoss
8
5
  module Calendars
9
6
  extend self
10
7
  extend Enumerable
11
8
  delegate :each, :length, to: :all
12
9
 
10
+ # Register a new calendar
11
+ # @return [Entry]
12
+ def register(name, klass)
13
+ Entry.new(name.to_sym, klass).tap do |entry|
14
+ (@entries ||= {})[name.to_sym] = entry
15
+ end
16
+ end
17
+
13
18
  # Retrieve a list of all registered calendars.
14
19
  # @return [Array<Entry>]
15
20
  def all
16
- @_all ||= TimeBoss::Calendar.subclasses.map do |klass|
17
- Entry.new(klass.to_s.demodulize.underscore.to_sym, klass)
18
- end
21
+ return if @entries.nil?
22
+ @entries.values.sort_by { |e| e.name.to_s }
19
23
  end
20
24
 
21
25
  # Retrieve an instance of the specified named calendar.
22
26
  # @param name [String, Symbol] the name of the calendar to retrieve.
23
27
  # @return [Calendar]
24
28
  def [](name)
25
- find { |e| e.name == name&.to_sym }&.calendar
29
+ return if @entries.nil?
30
+ @entries[name&.to_sym]&.calendar
26
31
  end
27
32
 
28
33
  private
@@ -44,3 +49,5 @@ module TimeBoss
44
49
  end
45
50
  end
46
51
  end
52
+
53
+ Dir[File.expand_path('../calendars/*.rb', __FILE__)].each { |f| require f }
@@ -4,6 +4,8 @@ require_relative '../calendar'
4
4
  module TimeBoss
5
5
  module Calendars
6
6
  class Broadcast < Calendar
7
+ register!
8
+
7
9
  def initialize
8
10
  super(basis: Basis)
9
11
  end
@@ -4,6 +4,8 @@ require_relative '../calendar'
4
4
  module TimeBoss
5
5
  module Calendars
6
6
  class Gregorian < Calendar
7
+ register!
8
+
7
9
  def initialize
8
10
  super(basis: Basis)
9
11
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module TimeBoss
3
- VERSION = "0.2.3"
3
+ VERSION = "1.0.0"
4
4
  end
@@ -493,10 +493,9 @@ module TimeBoss
493
493
  expect(date.end_date).to eq Date.parse('2017-04-08')
494
494
  end
495
495
 
496
- it 'gives you this year if you give it nothing' do
497
- year = subject.this_year
498
- expect(subject.parse(nil)).to eq year
499
- expect(subject.parse('')).to eq year
496
+ it 'gives you nothing if you give it nothing' do
497
+ expect(subject.parse(nil)).to be nil
498
+ expect(subject.parse('')).to be nil
500
499
  end
501
500
  end
502
501
 
@@ -475,10 +475,9 @@ module TimeBoss
475
475
  expect(date.end_date).to eq Date.parse('2017-04-08')
476
476
  end
477
477
 
478
- it 'gives you this year if you give it nothing' do
479
- year = subject.this_year
480
- expect(subject.parse(nil)).to eq year
481
- expect(subject.parse('')).to eq year
478
+ it 'gives you nothing if you give it nothing' do
479
+ expect(subject.parse(nil)).to be nil
480
+ expect(subject.parse('')).to be nil
482
481
  end
483
482
  end
484
483
 
@@ -1,11 +1,13 @@
1
1
  module TimeBoss
2
2
  describe Calendars do
3
- class MyTestCalendar < TimeBoss::Calendar
3
+ class MyCal < TimeBoss::Calendar
4
4
  def initialize
5
5
  super(basis: nil)
6
6
  end
7
7
  end
8
8
 
9
+ TimeBoss::Calendars.register(:my_amazing_calendar, MyCal)
10
+
9
11
  describe '#all' do
10
12
  let(:all) { described_class.all }
11
13
 
@@ -17,7 +19,7 @@ module TimeBoss
17
19
 
18
20
  context 'enumerability' do
19
21
  it 'can get a list of names' do
20
- expect(described_class.map(&:name)).to include(:broadcast, :my_test_calendar)
22
+ expect(described_class.map(&:name)).to include(:broadcast, :my_amazing_calendar)
21
23
  end
22
24
  end
23
25
  end
@@ -34,10 +36,10 @@ module TimeBoss
34
36
  end
35
37
 
36
38
  it 'can return a new calendar' do
37
- c1 = described_class[:my_test_calendar]
38
- expect(c1).to be_instance_of MyTestCalendar
39
- expect(c1.name).to eq 'my_test_calendar'
40
- expect(c1.title).to eq 'My Test Calendar'
39
+ c1 = described_class[:my_amazing_calendar]
40
+ expect(c1).to be_instance_of MyCal
41
+ expect(c1.name).to eq 'my_cal'
42
+ expect(c1.title).to eq 'My Cal'
41
43
  end
42
44
 
43
45
  it 'can graceully give you nothing' do
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.2.3
4
+ version: 1.0.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-20 00:00:00.000000000 Z
11
+ date: 2020-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport