timeboss 1.0.0 → 1.1.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/gem-push.yml +31 -0
  3. data/.github/workflows/ruby.yml +35 -0
  4. data/.travis.yml +3 -2
  5. data/Gemfile +2 -1
  6. data/README.md +17 -2
  7. data/Rakefile +3 -1
  8. data/bin/tbsh +2 -2
  9. data/lib/tasks/calendars.rake +5 -5
  10. data/lib/tasks/timeboss.rake +2 -2
  11. data/lib/timeboss/calendar/day.rb +3 -2
  12. data/lib/timeboss/calendar/half.rb +2 -1
  13. data/lib/timeboss/calendar/month.rb +2 -1
  14. data/lib/timeboss/calendar/parser.rb +9 -8
  15. data/lib/timeboss/calendar/period.rb +9 -6
  16. data/lib/timeboss/calendar/quarter.rb +2 -1
  17. data/lib/timeboss/calendar/support/formatter.rb +5 -4
  18. data/lib/timeboss/calendar/support/has_fiscal_weeks.rb +17 -0
  19. data/lib/timeboss/calendar/support/has_iso_weeks.rb +30 -0
  20. data/lib/timeboss/calendar/support/month_basis.rb +1 -1
  21. data/lib/timeboss/calendar/support/monthly_unit.rb +8 -8
  22. data/lib/timeboss/calendar/support/navigable.rb +2 -1
  23. data/lib/timeboss/calendar/support/shiftable.rb +12 -11
  24. data/lib/timeboss/calendar/support/translatable.rb +3 -2
  25. data/lib/timeboss/calendar/support/unit.rb +20 -13
  26. data/lib/timeboss/calendar/waypoints/absolute.rb +4 -3
  27. data/lib/timeboss/calendar/waypoints/relative.rb +14 -13
  28. data/lib/timeboss/calendar/week.rb +3 -2
  29. data/lib/timeboss/calendar/year.rb +2 -1
  30. data/lib/timeboss/calendar.rb +8 -7
  31. data/lib/timeboss/calendars/broadcast.rb +10 -7
  32. data/lib/timeboss/calendars/gregorian.rb +4 -5
  33. data/lib/timeboss/calendars.rb +3 -2
  34. data/lib/timeboss/version.rb +2 -1
  35. data/lib/timeboss.rb +2 -0
  36. data/spec/{calendar → lib/timeboss/calendar}/day_spec.rb +14 -14
  37. data/spec/{calendar → lib/timeboss/calendar}/quarter_spec.rb +9 -9
  38. data/spec/lib/timeboss/calendar/support/monthly_unit_spec.rb +91 -0
  39. data/spec/{calendar → lib/timeboss/calendar}/support/unit_spec.rb +23 -22
  40. data/spec/{calendar → lib/timeboss/calendar}/week_spec.rb +20 -20
  41. data/spec/lib/timeboss/calendars/broadcast_spec.rb +796 -0
  42. data/spec/lib/timeboss/calendars/gregorian_spec.rb +793 -0
  43. data/spec/{calendars_spec.rb → lib/timeboss/calendars_spec.rb} +19 -19
  44. data/spec/spec_helper.rb +2 -2
  45. data/timeboss.gemspec +16 -14
  46. metadata +51 -20
  47. data/lib/timeboss/support/shellable.rb +0 -17
  48. data/spec/calendar/support/monthly_unit_spec.rb +0 -85
  49. data/spec/calendars/broadcast_spec.rb +0 -796
  50. data/spec/calendars/gregorian_spec.rb +0 -684
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b0d4b9c100b0d59e5aa5b71152c390f59deedce0e071c0d5c82666dba445946d
4
- data.tar.gz: dc533dd7884b186571322d2229823f47da014a623a1f50535a7c723f4d6c355c
3
+ metadata.gz: 6d4780b44297b7a8ca671619625c7c868a1ca3293452c0be5e3fede1fc533da8
4
+ data.tar.gz: '0668065a605519760653ede09a54ce458e4ed78ec7a34bec5013a0fe7e4ffb2f'
5
5
  SHA512:
6
- metadata.gz: 0acc27cc7254a3e0d67df651ddf2462bb606e2e844e7076bf36fd9c5ce0eacb2ccf08963ee418f41204c60c6acd6d0783fed60871b4729b46870c6f133de28f3
7
- data.tar.gz: '0281773958cbad85a0b10997c3ed058797906a5fed2f5452a572922492cabf27021871ea018aec5e8b85e078803a750a7f004f1d2b04ec1d6518002c0552360a'
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: jUWa2Jx0HRVv7Dl0zYPTskHQ+RJGy2TUcBWOjyFbBUbeDKiRh8J/NnbMuCOvmL94htSaFk5lcs6k74Itu6jW12+KyT98OAnOardFPFuI0cHHhIMEhF6ms8EhyOCZ0vvR9zav11gfNzyF716qO09DQSDuoKWWNdhrYj8a5paLdbHuRqQiw3YUmnjVQ/v9SPYaZ7ziXC4X4tBxQhK2ujNsNfztSVrpYb6pnsXnt+hFQOnskzi0+er4854iymykeyNVgB71KgivrE3T55ZTXPPglshsZQE7M7FlYA5L3bwzR7SlA202yFYHB3hUN3obwO3BjO4OiAdKQnSosBHaXI13ZvCOz02AfEJhwoxkBXw1/2CvSQ12QQPVgqkpxWY03h+URk+gGHCV+JaqZPCV9excmVWg/IOQou0IJ6h84VFViyVc2aFVN2AIdoaG3NsppbV3j4VuLG6J2VVpNmUzN3pGgi5YSjVq9Hp+huXZFfWpnUgAxc680hKgcFajE3JNANG+NbNEeoDmITvZyV5kvJklgUKPsl2wNk+wY/GGrSBgNOVlyXqTWLZb/8Z/cSrN5DEc6D5BIS/SG95wlCRT8jUqbyEPjT+H77TH2ySfwWZzblLqbIxaLiZr3hX16oMdUx7Cya5LKLFgQBpU6cFUxvZX7SqHwjk1vnuG592Kxd9GkX4=
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
@@ -1,3 +1,4 @@
1
1
  # frozen_string_literal: true
2
- source 'https://rubygems.org'
2
+
3
+ source "https://rubygems.org"
3
4
  gemspec
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) [![Run on Repl.it](https://repl.it/badge/github/kevinstuffandthings/timeboss)](https://repl.it/github/kevinstuffandthings/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
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "bundler/setup"
3
4
  require "bundler/gem_tasks"
5
+ require "standard/rake"
4
6
 
5
- Dir.glob('lib/tasks/*.rake').each { |r| load r }
7
+ Dir.glob("lib/tasks/*.rake").each { |r| load r }
data/bin/tbsh CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'timeboss/calendars'
4
- require 'timeboss/support/shellable'
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
- TimeBoss::Support::Shellable.open(calendar)
15
+ Shellable.open(calendar)
@@ -1,18 +1,18 @@
1
- require './lib/timeboss/calendars'
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] => ['timeboss:init'] do |_, args|
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: ['timeboss:init'] do
14
- require 'timeboss/support/shellable'
15
- TimeBoss::Support::Shellable.open(entry.calendar)
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"]
@@ -1,6 +1,6 @@
1
1
  namespace :timeboss do
2
2
  task :init do
3
- require './lib/timeboss'
4
- require './lib/timeboss/calendars'
3
+ require "./lib/timeboss"
4
+ require "./lib/timeboss/calendars"
5
5
  end
6
6
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require_relative './support/unit'
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('%B %-d, %Y')
21
+ start_date.strftime("%B %-d, %Y")
21
22
  end
22
23
 
23
24
  alias_method :to_s, :name
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require_relative './support/monthly_unit'
2
+
3
+ require_relative "./support/monthly_unit"
3
4
 
4
5
  module TimeBoss
5
6
  class Calendar
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require_relative './support/monthly_unit'
2
+
3
+ require_relative "./support/monthly_unit"
3
4
 
4
5
  module TimeBoss
5
6
  class Calendar
@@ -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 || '').strip.length > 0
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, '0']
28
- period = parse_period(base&.strip) or raise InvalidPeriodIdentifierError
29
- period.offset(offset.gsub(/\s+/, '').to_i)
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.send(identifier) if calendar.respond_to?(identifier.to_s)
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 = if identifier.to_i == 0 then calendar.this_year else calendar.year(identifier.to_i) end
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.send(size.pluralize)[identifier.to_i - 1] or raise InvalidPeriodIdentifierError
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.send(message)
32
- text = "#{text} #{Parser::RANGE_DELIMITER} #{self.end.send(message)}" unless self.end == self.begin
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.send("#{size}_for", self.begin.start_date)
116
- build_entries entry
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 = send(size.pluralize)
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 .. end_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
- require_relative './support/monthly_unit'
2
+
3
+ require_relative "./support/monthly_unit"
3
4
 
4
5
  module TimeBoss
5
6
  class Calendar
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require_relative './translatable'
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 = 'year', unit.year.name
22
+ base, text = "year", unit.year.name
22
23
  periods.each do |period|
23
- sub = unit.send(period) or break
24
- index = sub.send("in_#{base}")
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
@@ -13,7 +13,7 @@ module TimeBoss
13
13
  end
14
14
 
15
15
  def to_range
16
- @_to_range ||= start_date .. end_date
16
+ @_to_range ||= start_date..end_date
17
17
  end
18
18
  end
19
19
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require_relative './unit'
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
- num_weeks = (((base.end_date - base.start_date) + 1) / 7.0).to_i
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.send(self.class.type, year_index + 1, 1)
39
+ calendar.public_send(self.class.type, year_index + 1, 1)
40
40
  else
41
- calendar.send(self.class.type, year_index, index + 1)
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.send(self.class.type, year_index - 1, max_index)
47
+ calendar.public_send(self.class.type, year_index - 1, max_index)
48
48
  else
49
- calendar.send(self.class.type, year_index, index - 1)
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.send(navigator)
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 = send(periods)
11
+ base = public_send(periods)
11
12
  return unless base.length == 1
12
- base.first.send(self.class.type.to_s.pluralize).find_index { |p| p == self } + 1
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 = send("in_#{period}") or return
17
- (calendar.send("this_#{period}") - offset).send(self.class.type.to_s.pluralize)[base_offset - 1]
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| send("#{periods}_ago", o * -1) }
21
+ define_method("#{periods}_ahead") { |o| public_send("#{periods}_ago", o * -1) }
21
22
 
22
- define_method("last_#{period}") { send("#{periods}_ago", 1) }
23
- define_method("this_#{period}") { send("#{periods}_ago", 0) }
24
- define_method("next_#{period}") { send("#{periods}_ahead", 1) }
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.send("#{periods}_for", self) }
12
+ define_method(periods) { calendar.public_send("#{periods}_for", self) }
12
13
 
13
14
  define_method(period) do |index = nil|
14
- entries = send(periods)
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