timeboss 1.0.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/gem-push.yml +31 -0
- data/.github/workflows/ruby.yml +35 -0
- data/.travis.yml +3 -2
- data/Gemfile +2 -1
- data/README.md +17 -2
- data/Rakefile +3 -1
- data/bin/tbsh +2 -2
- data/lib/tasks/calendars.rake +5 -5
- data/lib/tasks/timeboss.rake +2 -2
- data/lib/timeboss/calendar/day.rb +3 -2
- data/lib/timeboss/calendar/half.rb +2 -1
- data/lib/timeboss/calendar/month.rb +2 -1
- data/lib/timeboss/calendar/parser.rb +9 -8
- data/lib/timeboss/calendar/period.rb +9 -6
- data/lib/timeboss/calendar/quarter.rb +2 -1
- data/lib/timeboss/calendar/support/formatter.rb +5 -4
- data/lib/timeboss/calendar/support/has_fiscal_weeks.rb +17 -0
- data/lib/timeboss/calendar/support/has_iso_weeks.rb +30 -0
- data/lib/timeboss/calendar/support/month_basis.rb +1 -1
- data/lib/timeboss/calendar/support/monthly_unit.rb +8 -8
- data/lib/timeboss/calendar/support/navigable.rb +2 -1
- data/lib/timeboss/calendar/support/shiftable.rb +12 -11
- data/lib/timeboss/calendar/support/translatable.rb +3 -2
- data/lib/timeboss/calendar/support/unit.rb +20 -13
- data/lib/timeboss/calendar/waypoints/absolute.rb +4 -3
- data/lib/timeboss/calendar/waypoints/relative.rb +14 -13
- data/lib/timeboss/calendar/week.rb +3 -2
- data/lib/timeboss/calendar/year.rb +2 -1
- data/lib/timeboss/calendar.rb +8 -7
- data/lib/timeboss/calendars/broadcast.rb +10 -7
- data/lib/timeboss/calendars/gregorian.rb +4 -5
- data/lib/timeboss/calendars.rb +3 -2
- data/lib/timeboss/version.rb +2 -1
- data/lib/timeboss.rb +2 -0
- data/spec/{calendar → lib/timeboss/calendar}/day_spec.rb +14 -14
- data/spec/{calendar → lib/timeboss/calendar}/quarter_spec.rb +9 -9
- data/spec/lib/timeboss/calendar/support/monthly_unit_spec.rb +91 -0
- data/spec/{calendar → lib/timeboss/calendar}/support/unit_spec.rb +23 -22
- data/spec/{calendar → lib/timeboss/calendar}/week_spec.rb +20 -20
- data/spec/lib/timeboss/calendars/broadcast_spec.rb +796 -0
- data/spec/lib/timeboss/calendars/gregorian_spec.rb +793 -0
- data/spec/{calendars_spec.rb → lib/timeboss/calendars_spec.rb} +19 -19
- data/spec/spec_helper.rb +2 -2
- data/timeboss.gemspec +16 -14
- metadata +51 -20
- data/lib/timeboss/support/shellable.rb +0 -17
- data/spec/calendar/support/monthly_unit_spec.rb +0 -85
- data/spec/calendars/broadcast_spec.rb +0 -796
- data/spec/calendars/gregorian_spec.rb +0 -684
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d4780b44297b7a8ca671619625c7c868a1ca3293452c0be5e3fede1fc533da8
|
4
|
+
data.tar.gz: '0668065a605519760653ede09a54ce458e4ed78ec7a34bec5013a0fe7e4ffb2f'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ce4fe447803b784adea3a2f397d031a669fc158046659b98b0994e8f707707c32b87930815cf5cae2774b03950f801033c355dea4efaf685b0b9e23e099e65f
|
7
|
+
data.tar.gz: 4b73ad3248db6c0e4c359b433bf011c1ce20647a5611415b7b56969e96d2b5aa62ea33d5247491aced44fa0f2dfc529300deac36d1dbd20370482d0b48ef232b
|
@@ -0,0 +1,31 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
release:
|
5
|
+
types: [published]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
name: Build + Publish
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
permissions:
|
12
|
+
contents: read
|
13
|
+
packages: write
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v2
|
17
|
+
- name: Set up Ruby 2.6
|
18
|
+
uses: actions/setup-ruby@v1
|
19
|
+
with:
|
20
|
+
ruby-version: 2.6.x
|
21
|
+
|
22
|
+
- name: Publish to RubyGems
|
23
|
+
run: |
|
24
|
+
mkdir -p $HOME/.gem
|
25
|
+
touch $HOME/.gem/credentials
|
26
|
+
chmod 0600 $HOME/.gem/credentials
|
27
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
28
|
+
gem build *.gemspec
|
29
|
+
gem push *.gem
|
30
|
+
env:
|
31
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
2
|
+
# They are provided by a third-party and are governed by
|
3
|
+
# separate terms of service, privacy policy, and support
|
4
|
+
# documentation.
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
7
|
+
|
8
|
+
name: Ruby
|
9
|
+
|
10
|
+
on:
|
11
|
+
push:
|
12
|
+
branches: [ master ]
|
13
|
+
pull_request:
|
14
|
+
branches: [ master ]
|
15
|
+
|
16
|
+
jobs:
|
17
|
+
test:
|
18
|
+
|
19
|
+
runs-on: ubuntu-latest
|
20
|
+
strategy:
|
21
|
+
matrix:
|
22
|
+
ruby-version: ['2.7', '3.0']
|
23
|
+
|
24
|
+
steps:
|
25
|
+
- uses: actions/checkout@v2
|
26
|
+
- name: Set up Ruby
|
27
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
28
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
29
|
+
# uses: ruby/setup-ruby@v1
|
30
|
+
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
31
|
+
with:
|
32
|
+
ruby-version: ${{ matrix.ruby-version }}
|
33
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
34
|
+
- name: Run tests
|
35
|
+
run: bundle exec rspec
|
data/.travis.yml
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.4
|
4
3
|
- 2.5
|
5
4
|
- 2.6
|
5
|
+
- 2.7
|
6
|
+
- 3.0
|
6
7
|
script:
|
7
8
|
- bundle exec rspec
|
8
9
|
deploy:
|
9
10
|
provider: rubygems
|
10
11
|
api_key:
|
11
|
-
secure:
|
12
|
+
secure: f7s86k3ZRgaao32goumx0EFSquj8v8vwLBQP0uPd5lZ5OlDjUIbzAGCLpP4IARFf7MhYUUxWqwU7A0Z2MJHgmf4hT2VdVco4kGC4WztMgSI2JwY8Uo21/QJgI82jIfZ6yfSjF8OC3eh9irqJxXfhzspO9DY4p+nJkMJnpG5Y1e5FjS9zM3gS80TD9fauIMEi3fOLDNYEZ95SgjrkX2MHDYQWN1nfFlkRtybSHJ2u2Ad3Ulr5c/1gIoJviJCm8l5Bwo3MnvBtSuHHjFOaH9UTcmDUGpBjr7GMoqn3m053aB1F3ImYwL9+il0rtj+PE1lNaVbUM/QDKp8gDcbo433m8oMiGRpouz0fdIi95fqsshZmSU9sZX6HPiOuURXXwrjW7n3bj71+qZ7zWPTyZB8p3Y6ocp/r6Aj0ewELJksjnbYqSuyYv0o5sKTh2AUMawcqWAnDlZWgMq4UqKQiaWlhZMN1guQIWO6Xq9xdoiIxcqRUTJ7dUAGsfv+GIs2iPLvh20DHudYN70J5b4xzZLFgPQOJbTGlQQtC18m2PaYvcdsZ1qzttQIs0fcgeKno1Ltcman6/yqbAdKsSjifLUcdqHiWOUk5Dh5l4S+iSVazILVFFHwV89JI1+ipuS1nnIaRcmfIkV3GB+aXcbwwYc89mLkXBVmezs+scygK0KUhoyU=
|
12
13
|
gem: timeboss
|
13
14
|
on:
|
14
15
|
tags: true
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# TimeBoss
|
1
|
+
# TimeBoss ![Build Status](https://github.com/kevinstuffandthings/timeboss/actions/workflows/ruby.yml/badge.svg) [![Gem Version](https://badge.fury.io/rb/timeboss.svg)](https://badge.fury.io/rb/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
|
|
@@ -139,7 +139,7 @@ period = calendar.parse('2020W3..2020Q1')
|
|
139
139
|
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.
|
140
140
|
|
141
141
|
### REPL
|
142
|
-
To open a REPL for the broadcast calendar, use the `tbsh` executable, or the `timeboss:calendars:broadcast:repl` rake task.
|
142
|
+
To open a [REPL](https://repl.it/github/kevinstuffandthings/timeboss) locally for the broadcast calendar, use the `tbsh` executable, or the `timeboss:calendars:broadcast:repl` rake task.
|
143
143
|
|
144
144
|
For the Gregorian calendar (or any other implemented calendars), supply the name on the command line.
|
145
145
|
- `tbsh gregorian`
|
@@ -157,6 +157,12 @@ $ tbsh
|
|
157
157
|
=> ["2020M7", "2020M8", "2020M9", "2020M10", "2020M11", "2020M12", "2021M1", "2021M2", "2021M3", "2021M4", "2021M5", "2021M6", "2021M7", "2021M8", "2021M9"]
|
158
158
|
```
|
159
159
|
|
160
|
+
If you want to try things out locally without installing the gem or updating your ruby environment, you can use [Docker](https://docker.com):
|
161
|
+
|
162
|
+
```bash
|
163
|
+
$ docker run --rm -it ruby:3.0-slim /bin/bash -c "gem install timeboss shellable >/dev/null && tbsh"
|
164
|
+
```
|
165
|
+
|
160
166
|
_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!_
|
161
167
|
|
162
168
|
## Creating new calendars
|
@@ -164,9 +170,14 @@ To create a custom calendar, simply extend the `TimeBoss::Calendar` class, and i
|
|
164
170
|
|
165
171
|
```ruby
|
166
172
|
require 'timeboss/calendar'
|
173
|
+
require 'timeboss/calendar/support/has_fiscal_weeks'
|
167
174
|
|
168
175
|
module MyCalendars
|
169
176
|
class AugustFiscal < TimeBoss::Calendar
|
177
|
+
# The calendar we wish to implement has "fiscal weeks", meaning that the weeks start on
|
178
|
+
# the day of the containing period.
|
179
|
+
include TimeBoss::Calendar::Support::HasFiscalWeeks
|
180
|
+
|
170
181
|
def initialize
|
171
182
|
# For each calendar, operation, the class will be instantiated with an ordinal value
|
172
183
|
# for `year` and `month`. It is the instance's job to translate those ordinals into
|
@@ -231,3 +242,7 @@ With the new calendar implemented, it can be accessed in one of 2 ways:
|
|
231
242
|
calendar = TimeBoss::Calendars[:august_fiscal]
|
232
243
|
calendar.this_year
|
233
244
|
```
|
245
|
+
|
246
|
+
# Problems?
|
247
|
+
Please submit an [issue](https://github.com/kevinstuffandthings/timeboss/issues).
|
248
|
+
We'll figure out how to get you up and running with TimeBoss as smoothly as possible.
|
data/Rakefile
CHANGED
data/bin/tbsh
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'timeboss/calendars'
|
4
|
-
require '
|
4
|
+
require 'shellable'
|
5
5
|
|
6
6
|
calendar = if ARGV.length == 1
|
7
7
|
TimeBoss::Calendars[ARGV.first]
|
@@ -12,4 +12,4 @@ calendar = if ARGV.length == 1
|
|
12
12
|
abort "Unknown calendar" if calendar.nil?
|
13
13
|
|
14
14
|
puts "Active calendar: #{calendar.title}"
|
15
|
-
|
15
|
+
Shellable.open(calendar)
|
data/lib/tasks/calendars.rake
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
require
|
1
|
+
require "./lib/timeboss/calendars"
|
2
2
|
|
3
3
|
namespace :timeboss do
|
4
4
|
namespace :calendars do
|
5
5
|
TimeBoss::Calendars.each do |entry|
|
6
6
|
namespace entry.name do
|
7
7
|
desc "Evaluate an expression for the #{entry.name} calendar"
|
8
|
-
task :evaluate, %i[expression] => [
|
8
|
+
task :evaluate, %i[expression] => ["timeboss:init"] do |_, args|
|
9
9
|
puts entry.calendar.parse(args[:expression])
|
10
10
|
end
|
11
11
|
|
12
12
|
desc "Open a REPL with the #{entry.name} calendar"
|
13
|
-
task repl: [
|
14
|
-
require
|
15
|
-
|
13
|
+
task repl: ["timeboss:init"] do
|
14
|
+
require "shellable"
|
15
|
+
Shellable.open(entry.calendar)
|
16
16
|
end
|
17
17
|
|
18
18
|
task shell: ["timeboss:calendars:#{entry.name}:repl"]
|
data/lib/tasks/timeboss.rake
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require_relative "./support/unit"
|
3
4
|
|
4
5
|
module TimeBoss
|
5
6
|
class Calendar
|
@@ -17,7 +18,7 @@ module TimeBoss
|
|
17
18
|
# Get a "pretty" representation of this day.
|
18
19
|
# @return [String] (e.g. "August 3, 2020")
|
19
20
|
def title
|
20
|
-
start_date.strftime(
|
21
|
+
start_date.strftime("%B %-d, %Y")
|
21
22
|
end
|
22
23
|
|
23
24
|
alias_method :to_s, :name
|
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module TimeBoss
|
3
4
|
class Calendar
|
4
5
|
class Parser
|
5
|
-
RANGE_DELIMITER =
|
6
|
+
RANGE_DELIMITER = ".."
|
6
7
|
InvalidPeriodIdentifierError = Class.new(StandardError)
|
7
8
|
attr_reader :calendar
|
8
9
|
|
@@ -11,7 +12,7 @@ module TimeBoss
|
|
11
12
|
end
|
12
13
|
|
13
14
|
def parse(identifier = nil)
|
14
|
-
return nil unless (identifier ||
|
15
|
+
return nil unless (identifier || "").strip.length > 0
|
15
16
|
return parse_identifier(identifier) unless identifier&.include?(RANGE_DELIMITER)
|
16
17
|
bases = identifier.split(RANGE_DELIMITER).map { |i| parse_identifier(i.strip) } unless identifier.nil?
|
17
18
|
bases ||= [parse_identifier(nil)]
|
@@ -24,13 +25,13 @@ module TimeBoss
|
|
24
25
|
|
25
26
|
def parse_identifier(identifier)
|
26
27
|
captures = identifier&.match(/^([^-]+)(\s*[+-]\s*[0-9]+)$/)&.captures
|
27
|
-
base, offset = captures || [identifier,
|
28
|
-
period = parse_period(base&.strip)
|
29
|
-
period.offset(offset.gsub(/\s+/,
|
28
|
+
base, offset = captures || [identifier, "0"]
|
29
|
+
(period = parse_period(base&.strip)) || raise(InvalidPeriodIdentifierError)
|
30
|
+
period.offset(offset.gsub(/\s+/, "").to_i)
|
30
31
|
end
|
31
32
|
|
32
33
|
def parse_period(identifier)
|
33
|
-
return calendar.
|
34
|
+
return calendar.public_send(identifier) if calendar.respond_to?(identifier.to_s)
|
34
35
|
parse_term(identifier || Date.today.year.to_s)
|
35
36
|
end
|
36
37
|
|
@@ -38,13 +39,13 @@ module TimeBoss
|
|
38
39
|
return Day.new(calendar, Date.parse(identifier)) if identifier.match?(/^[0-9]{4}-?[01][0-9]-?[0-3][0-9]$/)
|
39
40
|
|
40
41
|
raise InvalidPeriodIdentifierError unless identifier.match?(/^[HQMWD0-9]+$/)
|
41
|
-
period =
|
42
|
+
period = identifier.to_i == 0 ? calendar.this_year : calendar.year(identifier.to_i)
|
42
43
|
%w[half quarter month week day].each do |size|
|
43
44
|
prefix = size[0].upcase
|
44
45
|
next unless identifier.include?(prefix)
|
45
46
|
junk, identifier = identifier.split(prefix)
|
46
47
|
raise InvalidPeriodIdentifierError if junk.match?(/\D/)
|
47
|
-
period = period.
|
48
|
+
(period = period.public_send(size.pluralize)[identifier.to_i - 1]) || raise(InvalidPeriodIdentifierError)
|
48
49
|
end
|
49
50
|
period
|
50
51
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module TimeBoss
|
3
4
|
class Calendar
|
4
5
|
class Period
|
@@ -28,8 +29,8 @@ module TimeBoss
|
|
28
29
|
|
29
30
|
%i[name title to_s].each do |message|
|
30
31
|
define_method(message) do
|
31
|
-
text = self.begin.
|
32
|
-
text = "#{text} #{Parser::RANGE_DELIMITER} #{self.end.
|
32
|
+
text = self.begin.public_send(message)
|
33
|
+
text = "#{text} #{Parser::RANGE_DELIMITER} #{self.end.public_send(message)}" unless self.end == self.begin
|
33
34
|
text
|
34
35
|
end
|
35
36
|
end
|
@@ -112,12 +113,14 @@ module TimeBoss
|
|
112
113
|
|
113
114
|
%w[day week month quarter half year].each do |size|
|
114
115
|
define_method(size.pluralize) do
|
115
|
-
entry = calendar.
|
116
|
-
build_entries
|
116
|
+
entry = calendar.public_send("#{size}_for", self.begin.start_date) || self.begin.public_send(size, 1)
|
117
|
+
entries = build_entries(entry)
|
118
|
+
entries.pop if size == "week" && self.end.next.public_send(size, 1) == entries.last
|
119
|
+
entries
|
117
120
|
end
|
118
121
|
|
119
122
|
define_method(size) do |index = nil|
|
120
|
-
entries =
|
123
|
+
entries = public_send(size.pluralize)
|
121
124
|
return entries[index - 1] unless index.nil?
|
122
125
|
return nil unless entries.length == 1
|
123
126
|
entries.first
|
@@ -127,7 +130,7 @@ module TimeBoss
|
|
127
130
|
# Express this period as a date range.
|
128
131
|
# @return [Range<Date, Date>]
|
129
132
|
def to_range
|
130
|
-
@_to_range ||= start_date
|
133
|
+
@_to_range ||= start_date..end_date
|
131
134
|
end
|
132
135
|
|
133
136
|
def inspect
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require_relative "./translatable"
|
3
4
|
|
4
5
|
module TimeBoss
|
5
6
|
class Calendar
|
@@ -18,10 +19,10 @@ module TimeBoss
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def to_s
|
21
|
-
base, text =
|
22
|
+
base, text = "year", unit.year.name
|
22
23
|
periods.each do |period|
|
23
|
-
sub = unit.
|
24
|
-
index = sub.
|
24
|
+
(sub = unit.public_send(period)) || break
|
25
|
+
index = sub.public_send("in_#{base}")
|
25
26
|
text += "#{period[0].upcase}#{index}"
|
26
27
|
base = period
|
27
28
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TimeBoss
|
4
|
+
class Calendar
|
5
|
+
module Support
|
6
|
+
module HasFiscalWeeks
|
7
|
+
def weeks_in(year:)
|
8
|
+
num_weeks = (((year.end_date - year.start_date) + 1) / 7.0).to_i
|
9
|
+
num_weeks.times.map do |i|
|
10
|
+
start_date = year.start_date + (i * 7).days
|
11
|
+
Week.new(self, start_date, start_date + 6.days)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TimeBoss
|
4
|
+
class Calendar
|
5
|
+
module Support
|
6
|
+
module HasIsoWeeks
|
7
|
+
def weeks_in(year:)
|
8
|
+
weeks = []
|
9
|
+
start_date = Date.commercial(year.year_index)
|
10
|
+
end_date = Date.commercial(year.next.year_index)
|
11
|
+
while start_date < end_date
|
12
|
+
weeks << Week.new(self, start_date, start_date + 6.days)
|
13
|
+
start_date += 7.days
|
14
|
+
end
|
15
|
+
weeks
|
16
|
+
end
|
17
|
+
|
18
|
+
class Week < Calendar::Week
|
19
|
+
def index
|
20
|
+
start_date.cweek
|
21
|
+
end
|
22
|
+
|
23
|
+
def year
|
24
|
+
calendar.year(start_date.cwyear)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require_relative "./unit"
|
3
4
|
|
4
5
|
module TimeBoss
|
5
6
|
class Calendar
|
@@ -22,10 +23,9 @@ module TimeBoss
|
|
22
23
|
# Get a list of weeks contained within this period.
|
23
24
|
# @return [Array<Week>]
|
24
25
|
def weeks
|
26
|
+
raise UnsupportedUnitError unless calendar.supports_weeks?
|
25
27
|
base = calendar.year(year_index)
|
26
|
-
|
27
|
-
num_weeks.times.map { |i| Week.new(calendar, base.start_date + (i * 7).days, base.start_date + ((i * 7) + 6).days) }
|
28
|
-
.select { |w| w.start_date.between?(start_date, end_date) }
|
28
|
+
calendar.weeks_in(year: base).select { |w| (w.dates & dates).count >= 4 }
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
@@ -36,17 +36,17 @@ module TimeBoss
|
|
36
36
|
|
37
37
|
def up
|
38
38
|
if index == max_index
|
39
|
-
calendar.
|
39
|
+
calendar.public_send(self.class.type, year_index + 1, 1)
|
40
40
|
else
|
41
|
-
calendar.
|
41
|
+
calendar.public_send(self.class.type, year_index, index + 1)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
45
|
def down
|
46
46
|
if index == 1
|
47
|
-
calendar.
|
47
|
+
calendar.public_send(self.class.type, year_index - 1, max_index)
|
48
48
|
else
|
49
|
-
calendar.
|
49
|
+
calendar.public_send(self.class.type, year_index, index - 1)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module TimeBoss
|
3
4
|
class Calendar
|
4
5
|
module Support
|
@@ -61,7 +62,7 @@ module TimeBoss
|
|
61
62
|
entry = self
|
62
63
|
while quantity > 0
|
63
64
|
entries << entry
|
64
|
-
entry = entry.
|
65
|
+
entry = entry.public_send(navigator)
|
65
66
|
quantity -= 1
|
66
67
|
end
|
67
68
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module TimeBoss
|
3
4
|
class Calendar
|
4
5
|
module Support
|
@@ -7,21 +8,21 @@ module TimeBoss
|
|
7
8
|
periods = period.pluralize
|
8
9
|
|
9
10
|
define_method("in_#{period}") do
|
10
|
-
base =
|
11
|
+
base = public_send(periods)
|
11
12
|
return unless base.length == 1
|
12
|
-
base.first.
|
13
|
+
base.first.public_send(self.class.type.to_s.pluralize).find_index { |p| p == self } + 1
|
13
14
|
end
|
14
15
|
|
15
16
|
define_method("#{periods}_ago") do |offset|
|
16
|
-
base_offset =
|
17
|
-
(calendar.
|
17
|
+
(base_offset = public_send("in_#{period}")) || return
|
18
|
+
(calendar.public_send("this_#{period}") - offset).public_send(self.class.type.to_s.pluralize)[base_offset - 1]
|
18
19
|
end
|
19
20
|
|
20
|
-
define_method("#{periods}_ahead") { |o|
|
21
|
+
define_method("#{periods}_ahead") { |o| public_send("#{periods}_ago", o * -1) }
|
21
22
|
|
22
|
-
define_method("last_#{period}") {
|
23
|
-
define_method("this_#{period}") {
|
24
|
-
define_method("next_#{period}") {
|
23
|
+
define_method("last_#{period}") { public_send("#{periods}_ago", 1) }
|
24
|
+
define_method("this_#{period}") { public_send("#{periods}_ago", 0) }
|
25
|
+
define_method("next_#{period}") { public_send("#{periods}_ahead", 1) }
|
25
26
|
end
|
26
27
|
|
27
28
|
alias_method :yesterday, :last_day
|
@@ -133,7 +134,7 @@ module TimeBoss
|
|
133
134
|
# Get the index-relative month 1 month forward.
|
134
135
|
# Returns nil if no single month can be identified.
|
135
136
|
# @return [Calendar::Month, nil]
|
136
|
-
|
137
|
+
|
137
138
|
### Quarters
|
138
139
|
|
139
140
|
# @!method in_quarter
|
@@ -167,7 +168,7 @@ module TimeBoss
|
|
167
168
|
# Get the index-relative quarter 1 quarter forward.
|
168
169
|
# Returns nil if no single quarter can be identified.
|
169
170
|
# @return [Calendar::Quarter, nil]
|
170
|
-
|
171
|
+
|
171
172
|
### Halves
|
172
173
|
|
173
174
|
# @!method in_half
|
@@ -201,7 +202,7 @@ module TimeBoss
|
|
201
202
|
# Get the index-relative half 1 half forward.
|
202
203
|
# Returns nil if no single half can be identified.
|
203
204
|
# @return [Calendar::Half, nil]
|
204
|
-
|
205
|
+
|
205
206
|
### Years
|
206
207
|
|
207
208
|
# @!method in_year
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module TimeBoss
|
3
4
|
class Calendar
|
4
5
|
module Support
|
@@ -8,10 +9,10 @@ module TimeBoss
|
|
8
9
|
PERIODS.each do |period|
|
9
10
|
periods = period.pluralize
|
10
11
|
|
11
|
-
define_method(periods) { calendar.
|
12
|
+
define_method(periods) { calendar.public_send("#{periods}_for", self) }
|
12
13
|
|
13
14
|
define_method(period) do |index = nil|
|
14
|
-
entries =
|
15
|
+
entries = public_send(periods)
|
15
16
|
return entries[index - 1] unless index.nil?
|
16
17
|
return nil unless entries.length == 1
|
17
18
|
entries.first
|