timeboss 0.2.2 → 0.3.1
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 +4 -4
- data/.replit +2 -0
- data/README.md +9 -5
- data/lib/tasks/calendars.rake +4 -2
- data/lib/timeboss/calendar.rb +7 -0
- data/lib/timeboss/calendar/parser.rb +2 -1
- data/lib/timeboss/calendar/period.rb +78 -7
- data/lib/timeboss/calendar/support/month_basis.rb +2 -0
- data/lib/timeboss/calendar/support/translatable.rb +8 -7
- data/lib/timeboss/calendars.rb +14 -7
- data/lib/timeboss/calendars/broadcast.rb +2 -0
- data/lib/timeboss/calendars/gregorian.rb +2 -0
- data/lib/timeboss/version.rb +1 -1
- data/spec/calendars/broadcast_spec.rb +3 -4
- data/spec/calendars/gregorian_spec.rb +3 -4
- data/spec/calendars_spec.rb +8 -6
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cfac166bdd92d5f95689183603c5af386100642526cea8cbc405765655a1eb26
|
|
4
|
+
data.tar.gz: 00dfb8323ed9317cb214f532a7431bb9dc40d0ca3027ecda5f922bf72949ff0d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bf61f841ed2aa51150656da6d849b01a44511e478a8a0012e0074ec9a85fee8569a0bd162c22f3105b33d565c863df556bbee8bb65ea8d9b938272ff4b702b76
|
|
7
|
+
data.tar.gz: 1dff56f1577ab2e91eb4b5fcfee2928979c261ae02812eec1d2ab0c8788bd7078fbba7f95aec56c830f219d3c07f92513402cd3bb9035233283ffabdcb17fb0e
|
data/.replit
ADDED
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# TimeBoss [](https://travis-ci.com/kevinstuffandthings/timeboss) [](https://badge.fury.io/rb/timeboss)
|
|
1
|
+
# TimeBoss [](https://travis-ci.com/kevinstuffandthings/timeboss) [](https://badge.fury.io/rb/timeboss) [](https://repl.it/github/kevinstuffandthings/timeboss)
|
|
2
2
|
|
|
3
3
|
A gem providing convenient navigation of the [Broadcast Calendar](https://en.wikipedia.org/wiki/Broadcast_calendar), the standard Gregorian calendar, and is easily extensible to support multiple financial calendars.
|
|
4
4
|
|
|
@@ -135,12 +135,12 @@ period = calendar.parse('2020W3..2020Q1')
|
|
|
135
135
|
|
|
136
136
|
The examples above are just samples. Try different periods, operators, etc. All of the non-week-based operations will work similarly on the `TimeBoss::Calendars::Gregorian` calendar.
|
|
137
137
|
|
|
138
|
-
###
|
|
139
|
-
To open
|
|
138
|
+
### REPL
|
|
139
|
+
To open a REPL for the broadcast calendar, use the `tbsh` executable, or the `timeboss:calendars:broadcast:repl` rake task.
|
|
140
140
|
|
|
141
141
|
For the Gregorian calendar (or any other implemented calendars), supply the name on the command line.
|
|
142
142
|
- `tbsh gregorian`
|
|
143
|
-
- `rake timeboss:calendars:gregorian:
|
|
143
|
+
- `rake timeboss:calendars:gregorian:repl`
|
|
144
144
|
|
|
145
145
|
You will find yourself in the context of an instantiated `TimeBoss::Calendar` object:
|
|
146
146
|
|
|
@@ -154,7 +154,7 @@ $ tbsh
|
|
|
154
154
|
=> ["2020M7", "2020M8", "2020M9", "2020M10", "2020M11", "2020M12", "2021M1", "2021M2", "2021M3", "2021M4", "2021M5", "2021M6", "2021M7", "2021M8", "2021M9"]
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
_Having trouble with the
|
|
157
|
+
_Having trouble with the REPL? If you are using the examples from the [Usage](#Usage) section, keep in mind that the REPL is already in the context of the calendar -- so you don't need to specify the receiver!_
|
|
158
158
|
|
|
159
159
|
## Creating new calendars
|
|
160
160
|
To create a custom calendar, simply extend the `TimeBoss::Calendar` class, and implement a new `TimeBoss::Calendar::Support::MonthBasis` for it.
|
|
@@ -221,6 +221,10 @@ With the new calendar implemented, it can be accessed in one of 2 ways:
|
|
|
221
221
|
|
|
222
222
|
```ruby
|
|
223
223
|
require 'timeboss/calendars'
|
|
224
|
+
TimeBoss::Calendars.register(:august_fiscal, MyCalendars::AugustFiscal)
|
|
225
|
+
|
|
226
|
+
# You'll get a cached instance of your calendar here.
|
|
227
|
+
# Handy when switching back and forth between different calendar implementations.
|
|
224
228
|
calendar = TimeBoss::Calendars[:august_fiscal]
|
|
225
229
|
calendar.this_year
|
|
226
230
|
```
|
data/lib/tasks/calendars.rake
CHANGED
|
@@ -9,11 +9,13 @@ namespace :timeboss do
|
|
|
9
9
|
puts entry.calendar.parse(args[:expression])
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
desc "Open a
|
|
13
|
-
task
|
|
12
|
+
desc "Open a REPL with the #{entry.name} calendar"
|
|
13
|
+
task repl: ['timeboss:init'] do
|
|
14
14
|
require 'timeboss/support/shellable'
|
|
15
15
|
TimeBoss::Support::Shellable.open(entry.calendar)
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
task shell: ["timeboss:calendars:#{entry.name}:repl"]
|
|
17
19
|
end
|
|
18
20
|
end
|
|
19
21
|
end
|
data/lib/timeboss/calendar.rb
CHANGED
|
@@ -25,6 +25,7 @@ module TimeBoss
|
|
|
25
25
|
def name
|
|
26
26
|
self.class.to_s.demodulize.underscore
|
|
27
27
|
end
|
|
28
|
+
alias_method :to_s, :name
|
|
28
29
|
|
|
29
30
|
# Get a friendly title for this calendar.
|
|
30
31
|
# @return [String]
|
|
@@ -40,6 +41,12 @@ module TimeBoss
|
|
|
40
41
|
true
|
|
41
42
|
end
|
|
42
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
|
+
|
|
43
50
|
protected
|
|
44
51
|
|
|
45
52
|
attr_reader :basis
|
|
@@ -11,7 +11,8 @@ module TimeBoss
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def parse(identifier = nil)
|
|
14
|
-
return
|
|
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,25 +34,96 @@ 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
|
|
@@ -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]
|
data/lib/timeboss/calendars.rb
CHANGED
|
@@ -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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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 }
|
data/lib/timeboss/version.rb
CHANGED
|
@@ -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
|
|
497
|
-
|
|
498
|
-
expect(subject.parse(
|
|
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
|
|
479
|
-
|
|
480
|
-
expect(subject.parse(
|
|
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
|
|
data/spec/calendars_spec.rb
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
module TimeBoss
|
|
2
2
|
describe Calendars do
|
|
3
|
-
class
|
|
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, :
|
|
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[:
|
|
38
|
-
expect(c1).to be_instance_of
|
|
39
|
-
expect(c1.name).to eq '
|
|
40
|
-
expect(c1.title).to eq 'My
|
|
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.
|
|
4
|
+
version: 0.3.1
|
|
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-
|
|
11
|
+
date: 2020-07-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -133,6 +133,7 @@ files:
|
|
|
133
133
|
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
|
134
134
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
|
135
135
|
- ".gitignore"
|
|
136
|
+
- ".replit"
|
|
136
137
|
- ".rspec"
|
|
137
138
|
- ".travis.yml"
|
|
138
139
|
- ".yardopts"
|