fat_core 5.1.0 → 5.3.0

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.
@@ -31,7 +31,7 @@ module Enumerable
31
31
  # end
32
32
  def each_with_flags
33
33
  # Test for beginless range
34
- return nil if is_a?(Range) && self.begin.nil?
34
+ return if is_a?(Range) && self.begin.nil?
35
35
 
36
36
  last_k = size - 1
37
37
  each_with_index do |v, k|
data/lib/fat_core/nil.rb CHANGED
@@ -8,7 +8,7 @@ module FatCore
8
8
  def as_string
9
9
  ''
10
10
  end
11
- alias entitle as_string
11
+ alias_method :entitle, :as_string
12
12
 
13
13
  # Allow nils to respond to #tex_quote for use in TeX documents
14
14
  #
@@ -15,13 +15,11 @@ module FatCore
15
15
  #
16
16
  # @return [Integer] -1, 0, or 1 for negative, zero or positive self
17
17
  def signum
18
- if positive?
19
- 1
20
- elsif negative?
21
- -1
22
- else
23
- 0
24
- end
18
+ raise NotImplementedError unless real?
19
+ return 1 if positive?
20
+ return -1 if negative?
21
+
22
+ 0
25
23
  end
26
24
 
27
25
  # Convert this number into a string and insert grouping commas into the
@@ -65,7 +63,7 @@ module FatCore
65
63
 
66
64
  # Only convert to string numbers with exponent unless they are
67
65
  # less than 1 (to ensure that really small numbers round to 0.0)
68
- return to_s if abs > 1.0 && to_s =~ /e/
66
+ return to_s if abs > 1.0 && to_s.include?('e')
69
67
 
70
68
  # Round if places given
71
69
  str =
@@ -135,13 +133,20 @@ module FatCore
135
133
  mins, secs = divmod(60)
136
134
  hrs, mins = mins.divmod(60)
137
135
  if frac.round(5) > 0.0
138
- format('%02<hrs>d:%02<mins>d:%02<secs>d.%<frac>d',
139
- { hrs: hrs,
140
- mins: mins, secs: secs,
141
- frac: frac.round(5) * 100 })
136
+ format(
137
+ '%02<hrs>d:%02<mins>d:%02<secs>d.%<frac>d',
138
+ {
139
+ hrs: hrs,
140
+ mins: mins,
141
+ secs: secs,
142
+ frac: frac.round(5) * 100
143
+ },
144
+ )
142
145
  else
143
- format('%02<hrs>d:%02<mins>d:%02<secs>d',
144
- { hrs: hrs, mins: mins, secs: secs })
146
+ format(
147
+ '%02<hrs>d:%02<mins>d:%02<secs>d',
148
+ { hrs: hrs, mins: mins, secs: secs },
149
+ )
145
150
  end
146
151
  end
147
152
 
@@ -128,11 +128,11 @@ module FatCore
128
128
  # @param other [Range] the Range self is intersected with
129
129
  # @return [Range, nil] a Range representing the intersection
130
130
  def intersection(other)
131
- return nil unless overlaps?(other)
131
+ return unless overlaps?(other)
132
132
 
133
133
  ([min, other.min].max..[max, other.max].min)
134
134
  end
135
- alias & intersection
135
+ alias_method :&, :intersection
136
136
 
137
137
  # Return a Range that represents the union between this range and the
138
138
  # `other` range. If there is no overlap and self is not contiguous with
@@ -146,11 +146,11 @@ module FatCore
146
146
  # @param other [Range] the Range self is union-ed with
147
147
  # @return [Range, nil] a Range representing the union
148
148
  def union(other)
149
- return nil unless overlaps?(other) || contiguous?(other)
149
+ return unless overlaps?(other) || contiguous?(other)
150
150
 
151
151
  ([min, other.min].min..[max, other.max].max)
152
152
  end
153
- alias + union
153
+ alias_method :+, :union
154
154
 
155
155
  # The difference method, -, removes the overlapping part of the other
156
156
  # argument from self. Because in the case where self is a superset of the
@@ -187,7 +187,7 @@ module FatCore
187
187
  [(min..isec.min.pred), (isec.max.succ..max)]
188
188
  end
189
189
  end
190
- alias - difference
190
+ alias_method :-, :difference
191
191
 
192
192
  # Allow erb or erubis documents to directly interpolate a Range.
193
193
  #
@@ -63,7 +63,7 @@ module FatCore
63
63
  result << w
64
64
  first_word_on_line = false
65
65
  line_width_so_far += 1 + w.length
66
- next unless line_width_so_far >= width
66
+ next if line_width_so_far < width
67
67
 
68
68
  result << "\n"
69
69
  line_width_so_far = 0
@@ -108,9 +108,11 @@ module FatCore
108
108
  # @return [Date] the translated Date
109
109
  def as_date
110
110
  if self =~ %r{(?<yr>\d\d\d\d)[-/]?(?<mo>\d\d?)[-/]?(?<dy>\d\d?)}
111
- ::Date.new(Regexp.last_match[:yr].to_i,
112
- Regexp.last_match[:mo].to_i,
113
- Regexp.last_match[:dy].to_i)
111
+ ::Date.new(
112
+ Regexp.last_match[:yr].to_i,
113
+ Regexp.last_match[:mo].to_i,
114
+ Regexp.last_match[:dy].to_i,
115
+ )
114
116
  end
115
117
  end
116
118
 
@@ -149,8 +151,26 @@ module FatCore
149
151
  #
150
152
  # @return [String]
151
153
  def entitle
152
- little_words = %w[a an the at for up and but
153
- or nor in on under of from as by to]
154
+ little_words = %w[
155
+ a
156
+ an
157
+ the
158
+ at
159
+ for
160
+ up
161
+ and
162
+ but
163
+ or
164
+ nor
165
+ in
166
+ on
167
+ under
168
+ of
169
+ from
170
+ as
171
+ by
172
+ to
173
+ ]
154
174
  preserve_acronyms = !all_upper?
155
175
  newwords = []
156
176
  capitalize_next = false
@@ -159,26 +179,26 @@ module FatCore
159
179
  words.each_with_index do |w, k|
160
180
  first = k.zero?
161
181
  last = (k == last_k)
162
- if w =~ %r{c/o}i
182
+ if %r{c/o}i.match?(w)
163
183
  # Care of
164
184
  newwords.push('c/o')
165
- elsif w =~ /^p\.?o\.?$/i
185
+ elsif /^p\.?o\.?$/i.match?(w)
166
186
  # Post office
167
187
  newwords.push('P.O.')
168
- elsif w =~ /^[0-9]+(st|nd|rd|th)$/i
188
+ elsif /^[0-9]+(st|nd|rd|th)$/i.match?(w)
169
189
  # Ordinals
170
190
  newwords.push(w.downcase)
171
- elsif w =~ /^(cr|dr|st|rd|ave|pk|cir)$/i
191
+ elsif /^(cr|dr|st|rd|ave|pk|cir)$/i.match?(w)
172
192
  # Common abbrs to capitalize
173
193
  newwords.push(w.capitalize)
174
- elsif w =~ /^(us|ne|se|rr)$/i
194
+ elsif /^(us|ne|se|rr)$/i.match?(w)
175
195
  # Common 2-letter abbrs to upcase
176
196
  newwords.push(w.upcase)
177
- elsif w =~ /^[0-9].*$/i
197
+ elsif /^[0-9].*$/i.match?(w)
178
198
  # Other runs starting with numbers,
179
199
  # like 3-A
180
200
  newwords.push(w.upcase)
181
- elsif w =~ /^(N|S|E|W|NE|NW|SE|SW)$/i
201
+ elsif /^(N|S|E|W|NE|NW|SE|SW)$/i.match?(w)
182
202
  # Compass directions all caps
183
203
  newwords.push(w.upcase)
184
204
  elsif w =~ /^[^aeiouy]*$/i && w.size > 2
@@ -202,7 +222,7 @@ module FatCore
202
222
  newwords.push(w.capitalize)
203
223
  end
204
224
  # Capitalize following a ':'
205
- capitalize_next = true if newwords.last =~ /:\s*\z/
225
+ capitalize_next = true if /:\s*\z/.match?(newwords.last)
206
226
  end
207
227
  newwords.join(' ')
208
228
  end
@@ -272,11 +292,11 @@ module FatCore
272
292
  matcher = matcher.strip.gsub(/[\*.,']/, '')
273
293
  if matcher.start_with?(':')
274
294
  begin_anchor = true
275
- matcher.sub!(/\A:/, '')
295
+ matcher.delete_prefix!(':')
276
296
  end
277
297
  if matcher.end_with?(':')
278
298
  end_anchor = true
279
- matcher.sub!(/:\z/, '')
299
+ matcher.delete_suffix!(':')
280
300
  end
281
301
  target = gsub(/[\*.,']/, '')
282
302
  matchers = matcher.split(/[: ]+/)
@@ -364,7 +384,7 @@ module FatCore
364
384
  # @return [String] commified number as a String
365
385
  def commas(places = nil)
366
386
  numeric_re = /\A([-+])?([\d_]*)((\.)?([\d_]*))?([eE][+-]?[\d_]+)?\z/
367
- return self unless clean =~ numeric_re
387
+ return self unless clean&.match?(numeric_re)
368
388
 
369
389
  to_f.commas(places)
370
390
  end
@@ -16,7 +16,7 @@ module FatCore
16
16
  def as_string
17
17
  to_s.tr('_', ' ').split.join(' ').entitle
18
18
  end
19
- alias entitle as_string
19
+ alias_method :entitle, :as_string
20
20
 
21
21
  # Return self. This (together with String#as_sym) allows `#as_sym` to be
22
22
  # applied to a string or Symbol and get back a Symbol with out testing for
@@ -2,7 +2,7 @@
2
2
 
3
3
  module FatCore
4
4
  MAJOR = 5
5
- MINOR = 1
5
+ MINOR = 3
6
6
  PATCH = 0
7
7
 
8
8
  # FatCore version number
@@ -23,21 +23,21 @@ describe Date do
23
23
  expect(described_class.ensure(described_class.today).class).to be described_class
24
24
  end
25
25
 
26
- it 'converts Time as a date' do
27
- expect(described_class.ensure_date(Time.now).class).to be described_class
28
- expect(described_class.ensure(Time.now).class).to be described_class
26
+ it 'converts Time as a DateTime' do
27
+ expect(described_class.ensure_date(Time.now).class).to be DateTime
28
+ expect(described_class.ensure(Time.now).class).to be DateTime
29
29
  end
30
30
 
31
31
  it 'raises an error for bad date string' do
32
- expect { described_class.ensure_date('2012-mm-tu') }.to raise_error(/invalid date/)
33
- expect { described_class.ensure('2012-mm-tu') }.to raise_error(/invalid date/)
32
+ expect { described_class.ensure_date('2012-mm-tu') }.to raise_error(/cannot convert string/)
33
+ expect { described_class.ensure('2012-mm-tu') }.to raise_error(/cannot convert string/)
34
34
  end
35
35
 
36
36
  it 'raises an error for unknown class' do
37
37
  expect { described_class.ensure_date([2011, 11, 12]) }
38
- .to raise_error(/requires String, Date, DateTime, or Time/)
38
+ .to raise_error(/cannot convert/)
39
39
  expect { described_class.ensure([2011, 11, 12]) }
40
- .to raise_error(/requires String, Date, DateTime, or Time/)
40
+ .to raise_error(/cannot convert/)
41
41
  end
42
42
  end
43
43
 
@@ -61,6 +61,32 @@ describe Date do
61
61
  expect { described_class.days_in_month(2004, 13) }.to raise_error(ArgumentError)
62
62
  end
63
63
 
64
+ it 'knows a Date\'s half year' do
65
+ expect(Date.parse('2024-05-14').half).to eq(1)
66
+ expect(Date.parse('2024-09-14').half).to eq(2)
67
+ end
68
+
69
+ it 'knows a Date\'s quarter year' do
70
+ expect(Date.parse('2024-03-14').quarter).to eq(1)
71
+ expect(Date.parse('2024-05-14').quarter).to eq(2)
72
+ expect(Date.parse('2024-09-14').quarter).to eq(3)
73
+ expect(Date.parse('2024-11-14').quarter).to eq(4)
74
+ end
75
+
76
+ it 'knows a Date\'s bimonth' do
77
+ expect(Date.parse('2024-03-14').bimonth).to eq(2)
78
+ expect(Date.parse('2024-05-14').bimonth).to eq(3)
79
+ expect(Date.parse('2024-09-14').bimonth).to eq(5)
80
+ expect(Date.parse('2024-11-14').bimonth).to eq(6)
81
+ end
82
+
83
+ it 'knows a Date\'s semimonth' do
84
+ expect(Date.parse('2024-03-14').semimonth).to eq(5)
85
+ expect(Date.parse('2024-05-14').semimonth).to eq(9)
86
+ expect(Date.parse('2024-09-24').semimonth).to eq(18)
87
+ expect(Date.parse('2024-11-14').semimonth).to eq(21)
88
+ end
89
+
64
90
  it 'knows the nth weekday in a year, month' do
65
91
  # Sunday is 0, Saturday is 6
66
92
  # January 2014
@@ -246,6 +272,14 @@ describe Date do
246
272
  expect(described_class.parse_spec('2011/08/05')).to eq described_class.parse('2011-08-05')
247
273
  end
248
274
 
275
+ it 'parses YYYY-ddd day-of-year dates correctly' do
276
+ expect(described_class.parse_spec('2011-115')).to eq described_class.parse('2011-04-25')
277
+ expect(described_class.parse_spec('2011/001')).to eq described_class.parse('2011-01-01')
278
+ expect {
279
+ described_class.parse_spec('2023-366')
280
+ }.to raise_error(/invalid day-of-year/)
281
+ end
282
+
249
283
  it "parses week numbers such as 'W23' or '23W' correctly" do
250
284
  expect(described_class.parse_spec('W1')).to eq described_class.parse('2012-01-02')
251
285
  expect(described_class.parse_spec('W23')).to eq described_class.parse('2012-06-04')
@@ -257,7 +291,19 @@ describe Date do
257
291
  }.to raise_error(ArgumentError)
258
292
  end
259
293
 
260
- it 'parse year-week numbers \'YYYY-W23\' correctly' do
294
+ it "parses week-day numbers such as 'W23-4' or '23W-3' correctly" do
295
+ expect(described_class.parse_spec('W1-4')).to eq described_class.parse('2012-01-05')
296
+ expect(described_class.parse_spec('W23-3')).to eq described_class.parse('2012-06-06')
297
+ expect(described_class.parse_spec('W23-3', :to)).to eq described_class.parse('2012-06-06')
298
+ expect(described_class.parse_spec('23W-2')).to eq described_class.parse('2012-06-05')
299
+ expect(described_class.parse_spec('23W-2', :to)).to eq described_class.parse('2012-06-05')
300
+ expect {
301
+ described_class.parse_spec('W38-9', :to)
302
+ }.to raise_error(ArgumentError)
303
+ end
304
+
305
+ it 'parse year-week numbers \'YYYY-Wnn\' correctly' do
306
+ expect(described_class.parse_spec('2002-W52')).to eq described_class.parse('2002-12-23')
261
307
  expect(described_class.parse_spec('2003-W1')).to eq described_class.parse('2002-12-30')
262
308
  expect(described_class.parse_spec('2003-W1', :to)).to eq described_class.parse('2003-01-05')
263
309
  expect(described_class.parse_spec('2003-W23')).to eq described_class.parse('2003-06-02')
@@ -269,6 +315,27 @@ describe Date do
269
315
  }.to raise_error(ArgumentError)
270
316
  end
271
317
 
318
+ it 'parse year-week-day numbers \'YYYY-Wnn-d\' correctly' do
319
+ # Examples from the Wikipedia page on ISO 8601 dates
320
+ expect(described_class.parse_spec('1976-W53-6')).to eq described_class.parse('1977-01-01')
321
+ expect(described_class.parse_spec('1977-W52-6')).to eq described_class.parse('1977-12-31')
322
+ expect(described_class.parse_spec('1977-W52-7')).to eq described_class.parse('1978-01-01')
323
+ expect(described_class.parse_spec('1978-W01-1')).to eq described_class.parse('1978-01-02')
324
+ expect(described_class.parse_spec('1978-W52-7')).to eq described_class.parse('1978-12-31')
325
+ expect(described_class.parse_spec('1979-W01-1')).to eq described_class.parse('1979-01-01')
326
+ expect(described_class.parse_spec('1979-W52-7')).to eq described_class.parse('1979-12-30')
327
+ expect(described_class.parse_spec('1980-W01-1')).to eq described_class.parse('1979-12-31')
328
+ expect(described_class.parse_spec('1980-W52-7')).to eq described_class.parse('1980-12-28')
329
+ expect(described_class.parse_spec('1981-W01-2')).to eq described_class.parse('1980-12-30')
330
+ expect(described_class.parse_spec('1981-W01-3')).to eq described_class.parse('1980-12-31')
331
+ expect(described_class.parse_spec('1981-W01-4')).to eq described_class.parse('1981-01-01')
332
+ expect(described_class.parse_spec('1981-W53-4')).to eq described_class.parse('1981-12-31')
333
+ expect(described_class.parse_spec('1981-W53-5')).to eq described_class.parse('1982-01-01')
334
+ expect {
335
+ described_class.parse_spec('2003-W83', :to)
336
+ }.to raise_error(ArgumentError)
337
+ end
338
+
272
339
  it 'parses year-half specs such as YYYY-NH or YYYY-HN' do
273
340
  expect(described_class.parse_spec('2011-2H', :from)).to eq described_class.parse('2011-07-01')
274
341
  expect(described_class.parse_spec('2011-2H', :to)).to eq described_class.parse('2011-12-31')
@@ -311,7 +378,8 @@ describe Date do
311
378
  expect(described_class.parse_spec('10', :from)).to eq described_class.parse('2012-10-01')
312
379
  expect(described_class.parse_spec('10', :to)).to eq described_class.parse('2012-10-31')
313
380
  expect { described_class.parse_spec('99') }.to raise_error(ArgumentError)
314
- expect { described_class.parse_spec('011') }.to raise_error(ArgumentError)
381
+ # This is a valid day-of-year spec
382
+ expect { described_class.parse_spec('011') }.not_to raise_error(ArgumentError)
315
383
  end
316
384
 
317
385
  it 'parses month-day specs such as MM-DD' do
@@ -335,11 +403,102 @@ describe Date do
335
403
  expect { described_class.parse_spec('99999') }.to raise_error(ArgumentError)
336
404
  end
337
405
 
406
+ it 'parses half-month specs such as YYYY-MM-I and YYYY-MM-II' do
407
+ expect(described_class.parse_spec('2010-09-I', :from)).to eq described_class.parse('2010-09-01')
408
+ expect(described_class.parse_spec('2010-09-I', :to)).to eq described_class.parse('2010-09-15')
409
+ expect(described_class.parse_spec('2010-09-II', :from)).to eq described_class.parse('2010-09-16')
410
+ expect(described_class.parse_spec('2010-09-II', :to)).to eq described_class.parse('2010-09-30')
411
+ end
412
+
413
+ it 'parses intra-month week specs such as YYYY-MM-i and YYYY-MM-v begin Sunday' do
414
+ expect(described_class.parse_spec('2010-09-i', :from)).to eq described_class.parse('2010-09-01')
415
+ expect(described_class.parse_spec('2010-09-ii', :from)).to eq described_class.parse('2010-09-06')
416
+ expect(described_class.parse_spec('2010-09-iii', :from)).to eq described_class.parse('2010-09-13')
417
+ expect(described_class.parse_spec('2010-09-iv', :from)).to eq described_class.parse('2010-09-20')
418
+ expect(described_class.parse_spec('2010-09-v', :from)).to eq described_class.parse('2010-09-27')
419
+ expect { described_class.parse_spec('2010-09-vi', :from) }.to raise_error(/no week/)
420
+ expect(described_class.parse_spec('2011-01-vi', :from)).to eq described_class.parse('2011-01-31')
421
+
422
+ expect(described_class.parse_spec('2010-09-i', :to)).to eq described_class.parse('2010-09-05')
423
+ expect(described_class.parse_spec('2010-09-ii', :to)).to eq described_class.parse('2010-09-12')
424
+ expect(described_class.parse_spec('2010-09-iii', :to)).to eq described_class.parse('2010-09-19')
425
+ expect(described_class.parse_spec('2010-09-iv', :to)).to eq described_class.parse('2010-09-26')
426
+ expect(described_class.parse_spec('2010-09-v', :to)).to eq described_class.parse('2010-09-30')
427
+ expect { described_class.parse_spec('2010-09-vi', :to) }.to raise_error(/no week/)
428
+ expect(described_class.parse_spec('2011-01-vi', :to)).to eq described_class.parse('2011-01-31')
429
+ end
430
+
431
+ it 'parses intra-month week specs such as YYYY-MM-i and YYYY-MM-v begin Monday' do
432
+ expect(described_class.parse_spec('2010-09-i', :from)).to eq described_class.parse('2010-09-01')
433
+ expect(described_class.parse_spec('2010-09-ii', :from)).to eq described_class.parse('2010-09-06')
434
+ expect(described_class.parse_spec('2010-09-iii', :from)).to eq described_class.parse('2010-09-13')
435
+ expect(described_class.parse_spec('2010-09-iv', :from)).to eq described_class.parse('2010-09-20')
436
+ expect(described_class.parse_spec('2010-09-v', :from)).to eq described_class.parse('2010-09-27')
437
+ expect { described_class.parse_spec('2010-09-vi', :from) }.to raise_error(/no week/)
438
+ expect { described_class.parse_spec('2010-10-vi', :from) }.to raise_error(/no week/)
439
+ expect(described_class.parse_spec('2011-01-vi', :to)).to eq described_class.parse('2011-01-31')
440
+
441
+ expect(described_class.parse_spec('2010-09-i', :to)).to eq described_class.parse('2010-09-05')
442
+ expect(described_class.parse_spec('2010-09-ii', :to)).to eq described_class.parse('2010-09-12')
443
+ expect(described_class.parse_spec('2010-09-iii', :to)).to eq described_class.parse('2010-09-19')
444
+ expect(described_class.parse_spec('2010-09-iv', :to)).to eq described_class.parse('2010-09-26')
445
+ expect(described_class.parse_spec('2010-09-v', :to)).to eq described_class.parse('2010-09-30')
446
+ expect { described_class.parse_spec('2010-09-vi', :to) }.to raise_error(/no week/)
447
+ expect { described_class.parse_spec('2010-09-vi', :to) }.to raise_error(/no week/)
448
+ expect(described_class.parse_spec('2011-01-vi', :to)).to eq described_class.parse('2011-01-31')
449
+
450
+ travel_to Time.local(2020, 9, 15)
451
+ expect(described_class.parse_spec('9-i', :from)).to eq described_class.parse('2020-09-01')
452
+ expect(described_class.parse_spec('09-i', :to)).to eq described_class.parse('2020-09-06')
453
+ expect(described_class.parse_spec('09-iii', :from)).to eq described_class.parse('2020-09-14')
454
+ expect(described_class.parse_spec('09-iii', :to)).to eq described_class.parse('2020-09-20')
455
+ expect(described_class.parse_spec('09-v', :from)).to eq described_class.parse('2020-09-28')
456
+ expect(described_class.parse_spec('09-v', :to)).to eq described_class.parse('2020-09-30')
457
+
458
+ expect(described_class.parse_spec('i', :to)).to eq described_class.parse('2020-09-06')
459
+ expect(described_class.parse_spec('ii', :to)).to eq described_class.parse('2020-09-13')
460
+ expect(described_class.parse_spec('iii', :to)).to eq described_class.parse('2020-09-20')
461
+ expect(described_class.parse_spec('iv', :to)).to eq described_class.parse('2020-09-27')
462
+ expect(described_class.parse_spec('v', :to)).to eq described_class.parse('2020-09-30')
463
+ travel_back
464
+ end
465
+
466
+ it 'parses Easter-relative dates' do
467
+ expect(described_class.parse_spec('2024-E')).to eq described_class.parse('2024-03-31')
468
+ expect(described_class.parse_spec('2024-E+12')).to eq described_class.parse('2024-04-12')
469
+ expect(described_class.parse_spec('2024-E-12')).to eq described_class.parse('2024-03-19')
470
+ travel_to Time.local(2020, 9, 15)
471
+ expect(described_class.parse_spec('E-12')).to eq described_class.parse('2020-03-31')
472
+ expect(described_class.parse_spec('E+12')).to eq described_class.parse('2020-04-24')
473
+ travel_back
474
+ end
475
+
476
+ it 'parses DOW ordinals' do
477
+ expect(described_class.parse_spec('2024-11-4Th')).to eq described_class.parse('2024-11-28')
478
+ expect { described_class.parse_spec('2024-11-40Th') }.to raise_error(/invalid ordinal/)
479
+ expect { described_class.parse_spec('2024-15-4Th') }.to raise_error(/invalid month/)
480
+ expect { described_class.parse_spec('2024-11-5Th') }.to raise_error(/there is no 5th/i)
481
+ travel_to Time.local(2020, 9, 15)
482
+ expect(described_class.parse_spec('11-4Th')).to eq described_class.parse('2020-11-26')
483
+ expect(described_class.parse_spec('4Th')).to eq described_class.parse('2020-09-24')
484
+ travel_back
485
+ end
486
+
487
+ it 'parses DOY three-digit specs' do
488
+ expect(described_class.parse_spec('2024-001')).to eq described_class.parse('2024-01-01')
489
+ expect(described_class.parse_spec('2024-366')).to eq described_class.parse('2024-12-31')
490
+ expect { described_class.parse_spec('2024-885') }.to raise_error(/invalid day-of-year/)
491
+ travel_to Time.local(2020, 9, 15)
492
+ expect(described_class.parse_spec('150')).to eq described_class.parse('2020-05-29')
493
+ travel_back
494
+ end
495
+
338
496
  it 'parses relative day names: today, yesterday' do
339
497
  expect(described_class.parse_spec('today')).to eq described_class.current
340
498
  expect(described_class.parse_spec('this_day')).to eq described_class.current
341
499
  expect(described_class.parse_spec('yesterday')).to eq described_class.current - 1.day
342
500
  expect(described_class.parse_spec('last_day')).to eq described_class.current - 1.day
501
+ expect(described_class.parse_spec('tomorrow')).to eq described_class.current + 1.day
343
502
  end
344
503
 
345
504
  it 'parses relative weeks: this_week, last_week' do
@@ -445,7 +604,7 @@ describe Date do
445
604
  expect(described_class.mo_name_to_num("octagon")).to eq 10
446
605
  expect(described_class.mo_name_to_num(" novena this month")).to eq 11
447
606
  expect(described_class.mo_name_to_num("decimal")).to eq 12
448
- expect(described_class.mo_name_to_num(" dewey decimal")).to be nil
607
+ expect(described_class.mo_name_to_num(" dewey decimal")).to be_nil
449
608
  end
450
609
  end
451
610
  end
@@ -704,7 +863,6 @@ describe Date do
704
863
  .to be false
705
864
  expect(described_class.parse('2013-11-04').beginning_of_chunk?(:week))
706
865
  .to be true
707
- # Sunday is not beginning of commercial week
708
866
  expect(described_class.parse('2013-11-03').beginning_of_chunk?(:week))
709
867
  .to be false
710
868
  expect(described_class.parse('2013-11-01').beginning_of_chunk?(:day))
@@ -758,8 +916,9 @@ describe Date do
758
916
  .to be false
759
917
  expect(described_class.parse('2013-11-04').end_of_chunk?(:week))
760
918
  .to be false
761
- # Sunday is not end of commercial week
762
- expect(described_class.parse('2013-11-03').end_of_chunk?(:week))
919
+ expect(described_class.parse('2013-11-09').end_of_chunk?(:week))
920
+ .to be false
921
+ expect(described_class.parse('2013-11-10').end_of_chunk?(:week))
763
922
  .to be true
764
923
  expect(described_class.parse('2013-11-01').end_of_chunk?(:day))
765
924
  .to be true
@@ -4,7 +4,7 @@ require 'fat_core/enumerable'
4
4
  describe Enumerable do
5
5
  it 'enumerates groups of size k' do
6
6
  letters = ('a'..'z').to_a
7
- letters.groups_of(3).each do |k, grp|
7
+ letters.groups_of(3).each do |_k, grp|
8
8
  expect(grp.class).to eq Array
9
9
  if grp.last == 'z'
10
10
  expect(grp.size).to eq(2)
@@ -34,14 +34,17 @@ describe Enumerable do
34
34
 
35
35
  it 'returns nil enumerating a beginless Range' do
36
36
  bless = (..100)
37
- result = bless.each_with_flags { |l, first, last| 44 }
38
- expect(result).to be nil
37
+ result = bless.each_with_flags { |_l, _first, _last| 44 }
38
+ expect(result).to be_nil
39
39
  end
40
40
 
41
41
  it 'enumerates an endless Range' do
42
42
  eless = (1..)
43
43
  num = 0
44
- eless.each_with_flags { |i, first, last| num += i; break if i >= 100 }
44
+ eless.each_with_flags do |i, _first, _last|
45
+ num += i
46
+ break if i >= 100
47
+ end
45
48
  # Look at me, I'm Gauss
46
49
  expect(num).to eq(5050)
47
50
  end
@@ -3,11 +3,11 @@ require 'fat_core/kernel'
3
3
 
4
4
  describe Kernel do
5
5
  it 'knows how to time a block of code' do
6
- $stdout = StringIO.new
7
- result = time_it 'do something' do
8
- (1..10_000_000).each { |k| k * k }
9
- 'hello'
10
- end
11
- expect(result).to eq('hello')
6
+ expect {
7
+ time_it 'do something' do
8
+ (1..10_000_000).each { |k| k * k }
9
+ 'hello'
10
+ end
11
+ }.to output(/Ran do something in/i).to_stdout
12
12
  end
13
13
  end
@@ -74,8 +74,10 @@ describe Range do
74
74
 
75
75
  it 'knows the difference with another range' do
76
76
  # Other is same as self
77
+ # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
77
78
  expect(((4..10) - (4..10)).size).to eq(0)
78
79
  expect(((4..10) - (4..10))).to be_empty
80
+ # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands
79
81
 
80
82
  # Other is proper subset of self
81
83
  expect(((4..10) - (6..7)).first).to eq((4..5))
@@ -129,7 +131,7 @@ describe Range do
129
131
  end
130
132
 
131
133
  it 'can determine that overlapping ranges do not span' do
132
- expect((0..10)).to_not be_spanned_by([(0..3), (3..6), (7..10)])
134
+ expect((0..10)).not_to be_spanned_by([(0..3), (3..6), (7..10)])
133
135
  end
134
136
 
135
137
  it 'allows spanning ranges to be any Enumerable' do
@@ -137,7 +139,7 @@ describe Range do
137
139
  set = [(0..3), (4..6), (7..10)].to_set
138
140
  expect((0..10)).to be_spanned_by(set)
139
141
  set = [(0...3), (4..6), (7..10)].to_set
140
- expect((0..10)).to_not be_spanned_by(set)
142
+ expect((0..10)).not_to be_spanned_by(set)
141
143
  end
142
144
 
143
145
  it 'allows the spanning set to be wider than itself' do
@@ -149,20 +151,20 @@ describe Range do
149
151
 
150
152
  describe 'overlapping a single range' do
151
153
  it 'knows if another range overlaps it' do
152
- expect((0..10).overlaps?(-3..5)).to be_truthy
153
- expect((0..10).overlaps?(3..5)).to be_truthy
154
- expect((0..10).overlaps?(8..15)).to be_truthy
155
- expect((0..10).overlaps?(0..10)).to be_truthy
156
- expect((0..10).overlaps?(11..12)).to be_falsy
157
- expect((0..10).overlaps?(-11..-1)).to be_falsy
154
+ expect((0..10)).to be_overlaps(-3..5)
155
+ expect((0..10)).to be_overlaps(3..5)
156
+ expect((0..10)).to be_overlaps(8..15)
157
+ expect((0..10)).to be_overlaps(0..10)
158
+ expect((0..10)).not_to be_overlaps(11..12)
159
+ expect((0..10)).not_to be_overlaps(-11..-1)
158
160
 
159
161
  # Order of operands should not matter
160
- expect((-3..5).overlaps?(0..10)).to be_truthy
161
- expect((3..5).overlaps?(0..10)).to be_truthy
162
- expect((8..15).overlaps?(0..10)).to be_truthy
163
- expect((0..10).overlaps?(0..10)).to be_truthy
164
- expect((11..12).overlaps?(0..10)).to be_falsy
165
- expect((-11..-1).overlaps?(0..10)).to be_falsy
162
+ expect((-3..5)).to be_overlaps(0..10)
163
+ expect((3..5)).to be_overlaps(0..10)
164
+ expect((8..15)).to be_overlaps(0..10)
165
+ expect((0..10)).to be_overlaps(0..10)
166
+ expect((11..12)).not_to be_overlaps(0..10)
167
+ expect((-11..-1)).not_to be_overlaps(0..10)
166
168
  end
167
169
 
168
170
  it 'can determine whether a set contains covered overlaps' do