timeboss 0.2.3 → 1.0.0

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