timeboss 0.2.2 → 0.3.1

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: 9d4c953bb1ef449bdc7d37f0ac3061da752b28a9f4df8c1901f9507c447ac89b
4
- data.tar.gz: 752abd38f0afee607b6529894eec4189dfbbc462f5a27ddb4a6238720db35b4e
3
+ metadata.gz: cfac166bdd92d5f95689183603c5af386100642526cea8cbc405765655a1eb26
4
+ data.tar.gz: 00dfb8323ed9317cb214f532a7431bb9dc40d0ca3027ecda5f922bf72949ff0d
5
5
  SHA512:
6
- metadata.gz: b1d04714bcb3514b386fda20f1aaad42f1db96382719848ef9ee5cf889b5c855a26bbb6c6039f50e08ce2484194924ceb8a2ee32f0242b1e3bbc928a9156a0b8
7
- data.tar.gz: 1662c699fc80d9b37744a2f7f4fffa5d72e64d912bab4e19b1d113ba466a16201c003e32b26d1d8697da8c2feb21d4e16be344bc2bacdb2358f2a46ed6646c49
6
+ metadata.gz: bf61f841ed2aa51150656da6d849b01a44511e478a8a0012e0074ec9a85fee8569a0bd162c22f3105b33d565c863df556bbee8bb65ea8d9b938272ff4b702b76
7
+ data.tar.gz: 1dff56f1577ab2e91eb4b5fcfee2928979c261ae02812eec1d2ab0c8788bd7078fbba7f95aec56c830f219d3c07f92513402cd3bb9035233283ffabdcb17fb0e
data/.replit ADDED
@@ -0,0 +1,2 @@
1
+ language = "ruby"
2
+ run = "bundle exec rake timeboss:calendars:broadcast:repl"
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # TimeBoss [![Build Status](https://travis-ci.com/kevinstuffandthings/timeboss.svg?branch=master)](https://travis-ci.com/kevinstuffandthings/timeboss) [![Gem Version](https://badge.fury.io/rb/timeboss.svg)](https://badge.fury.io/rb/timeboss)
1
+ # TimeBoss [![Build Status](https://travis-ci.com/kevinstuffandthings/timeboss.svg?branch=master)](https://travis-ci.com/kevinstuffandthings/timeboss) [![Gem Version](https://badge.fury.io/rb/timeboss.svg)](https://badge.fury.io/rb/timeboss) [![Run on Repl.it](https://repl.it/badge/github/kevinstuffandthings/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
- ### Shell
139
- To open an IRB shell for the broadcast calendar, use the `tbsh` executable, or the `timeboss:calendars:broadcast:shell` rake task.
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:shell`
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 shell? If you are using the examples from the [Usage](#Usage) section, keep in mind that the shell is already in the context of the calendar -- so you don't need to specify the receiver!_
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
  ```
@@ -9,11 +9,13 @@ namespace :timeboss do
9
9
  puts entry.calendar.parse(args[:expression])
10
10
  end
11
11
 
12
- desc "Open a shell with the #{entry.name} calendar"
13
- task shell: ['timeboss:init'] do
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
@@ -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 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,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]
@@ -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.2"
3
+ VERSION = "0.3.1"
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.2
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-19 00:00:00.000000000 Z
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"