fat_core 1.0.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,6 @@ module Enumerable
2
2
  # Emit item in groups of n
3
3
  def groups_of(n)
4
4
  k = -1
5
- group_by { |item| k += 1; k.div(n) }
5
+ group_by { k += 1; k.div(n) }
6
6
  end
7
7
  end
@@ -0,0 +1,43 @@
1
+ module FatCore
2
+ # The Evaluator class provides a class for evaluating Ruby expressions based
3
+ # on variable settings provided at runtime. If the same Evaluator object is
4
+ # used for several successive calls, it can maintain state between calls with
5
+ # instance variables. The call to Evaluator.new can be given a hash of
6
+ # instance variable names and values that will be maintained across all calls
7
+ # to the #evaluate method. In addition, on each evaluate call, a set of
8
+ # /local/ variables can be supplied to provide variables that exist only for
9
+ # the duration of that evaluate call. An optional before and after string can
10
+ # be given to the constructor that will evaluate the given expression before
11
+ # and, respectively, after each call to #evaluate. This provides a way to
12
+ # update values of instance variables for use in subsequent calls to
13
+ # #evaluate.
14
+ class Evaluator
15
+ def initialize(vars: {}, before: nil, after: nil)
16
+ @before = before
17
+ @after = after
18
+ set_instance_vars(vars)
19
+ end
20
+
21
+ def set_instance_vars(vars = {})
22
+ vars.each_pair do |name, val|
23
+ name = "@#{name}" unless name.to_s.start_with?('@')
24
+ instance_variable_set(name, val)
25
+ end
26
+ end
27
+
28
+ def set_local_vars(vars = {}, bnd)
29
+ vars.each_pair do |name, val|
30
+ bnd.local_variable_set(name, val)
31
+ end
32
+ end
33
+
34
+ def evaluate(expr = '', vars: {})
35
+ bdg = binding
36
+ set_local_vars(vars, bdg)
37
+ eval(@before, bdg) if @before
38
+ result = eval(expr, bdg)
39
+ eval(@after, bdg) if @after
40
+ result
41
+ end
42
+ end
43
+ end
data/lib/fat_core/hash.rb CHANGED
@@ -23,7 +23,7 @@ class Hash
23
23
  def remap_keys(key_map = {})
24
24
  new_hash = {}
25
25
  each_pair do |key, val|
26
- if key_map.has_key?(key)
26
+ if key_map.key?(key)
27
27
  new_hash[key_map[key]] = val
28
28
  else
29
29
  new_hash[key] = val
data/lib/fat_core/nil.rb CHANGED
@@ -10,4 +10,8 @@ class NilClass
10
10
  def commas(_places = nil)
11
11
  ''
12
12
  end
13
+
14
+ def format_by(_fmt)
15
+ ''
16
+ end
13
17
  end
@@ -13,11 +13,12 @@ class Numeric
13
13
  # By default, use zero places for whole numbers; four places for
14
14
  # numbers containing a fractional part to 4 places.
15
15
  if places.nil?
16
- if self.abs.modulo(1).round(4) > 0.0
17
- places = 4
18
- else
19
- places = 0
20
- end
16
+ places =
17
+ if abs.modulo(1).round(4) > 0.0
18
+ 4
19
+ else
20
+ 0
21
+ end
21
22
  end
22
23
  group(places, ',')
23
24
  end
@@ -29,11 +30,9 @@ class Numeric
29
30
 
30
31
  # Only convert to string numbers with exponent unless they are
31
32
  # less than 1 (to ensure that really small numbers round to 0.0)
32
- if self.abs > 1.0 && self.to_s =~ /e/
33
- return self.to_s
34
- end
33
+ return to_s if abs > 1.0 && to_s =~ /e/
35
34
 
36
- str = self.to_f.round(places).to_s
35
+ str = to_f.round(places).to_s
37
36
 
38
37
  # Break the number into parts
39
38
  str =~ /^(-)?(\d*)((\.)?(\d*))?$/
@@ -43,7 +42,7 @@ class Numeric
43
42
 
44
43
  # Pad out the fractional part with zeroes to the right
45
44
  n_zeroes = [places - frac.length, 0].max
46
- frac += "0" * n_zeroes if n_zeroes > 0
45
+ frac += '0' * n_zeroes if n_zeroes > 0
47
46
 
48
47
  # Place the commas in the whole part only
49
48
  whole = whole.reverse
@@ -51,31 +50,45 @@ class Numeric
51
50
  whole.gsub!(/#{Regexp.escape(delim)}$/, '')
52
51
  whole.reverse!
53
52
  if frac.nil? || places <= 0
54
- return neg + whole
53
+ neg + whole
55
54
  else
56
- return neg + whole + '.' + frac
55
+ neg + whole + '.' + frac
57
56
  end
58
57
  end
59
58
 
60
59
  # Determine if this is a whole number.
61
60
  def whole?
62
- self.floor == self
61
+ floor == self
63
62
  end
64
63
 
65
64
  # Return an integer type, but only if the fractional part of self
66
65
  # is zero
67
66
  def int_if_whole
68
- whole? ? self.floor : self
67
+ whole? ? floor : self
69
68
  end
70
69
 
71
70
  def secs_to_hms
72
71
  frac = self % 1
73
- mins, secs = self.divmod(60)
72
+ mins, secs = divmod(60)
74
73
  hrs, mins = mins.divmod(60)
75
74
  if frac.round(5) > 0.0
76
- "%02d:%02d:%02d.%d" % [hrs, mins, secs, frac.round(5) * 100]
75
+ '%02d:%02d:%02d.%d' % [hrs, mins, secs, frac.round(5) * 100]
76
+ else
77
+ '%02d:%02d:%02d' % [hrs, mins, secs]
78
+ end
79
+ end
80
+
81
+ # Format the number according to the given sprintf format. Besides the
82
+ # sprintf formats, a format string of '%,2', for example, will return the
83
+ # number grouped by commas and rounded to 2 places. If no number of places
84
+ # is given, the number will be rounded to an integer.
85
+ def format_by(fmt = nil)
86
+ return to_s unless fmt
87
+ if /%,(?<places>\d*)/ =~ fmt.to_s.clean
88
+ places ||= 0
89
+ commas(places.to_i)
77
90
  else
78
- "%02d:%02d:%02d" % [hrs, mins, secs]
91
+ format fmt, self
79
92
  end
80
93
  end
81
94
 
@@ -21,7 +21,7 @@ class Period
21
21
  when Date
22
22
  first = first
23
23
  else
24
- raise ArgumentError, "use Date or String to initialize Period"
24
+ raise ArgumentError, 'use Date or String to initialize Period'
25
25
  end
26
26
 
27
27
  case last
@@ -38,7 +38,7 @@ class Period
38
38
  when Date
39
39
  last = last
40
40
  else
41
- raise ArgumentError, "use Date or String to initialize Period"
41
+ raise ArgumentError, 'use Date or String to initialize Period'
42
42
  end
43
43
 
44
44
  @first = first
@@ -54,21 +54,21 @@ class Period
54
54
 
55
55
  # Need custom setters to ensure first <= last
56
56
  def first=(new_first)
57
- unless new_first.kind_of?(Date)
57
+ unless new_first.is_a?(Date)
58
58
  raise ArgumentError, "can't set Period#first to non-date"
59
59
  end
60
60
  unless new_first <= last
61
- raise ArgumentError, "cannot make Period#first > Period#last"
61
+ raise ArgumentError, 'cannot make Period#first > Period#last'
62
62
  end
63
63
  @first = new_first
64
64
  end
65
65
 
66
66
  def last=(new_last)
67
- unless new_last.kind_of?(Date)
68
- raise ArgumentError, "can't set Period#last to non-date"
67
+ unless new_last.is_a?(Date)
68
+ raise ArgumentError, 'cannot set Period#last to non-date'
69
69
  end
70
70
  unless new_last >= first
71
- raise ArgumentError, "cannot make Period#last < Period#first"
71
+ raise ArgumentError, 'cannot make Period#last < Period#first'
72
72
  end
73
73
  @last = new_last
74
74
  end
@@ -91,13 +91,13 @@ class Period
91
91
  d = first
92
92
  while d <= last
93
93
  yield d
94
- d = d + 1.day
94
+ d += 1.day
95
95
  end
96
96
  end
97
97
 
98
98
  # Case equality checks for inclusion of date in period.
99
- def ===(date)
100
- self.contains?(date)
99
+ def ===(other)
100
+ contains?(other)
101
101
  end
102
102
 
103
103
  # Return the number of days in the period
@@ -146,13 +146,13 @@ class Period
146
146
  def self.parse_phrase(phrase)
147
147
  phrase = phrase.clean
148
148
  if phrase =~ /\Afrom (.*) to (.*)\z/
149
- from_phrase = $~[1]
150
- to_phrase = $~[2]
149
+ from_phrase = $1
150
+ to_phrase = $2
151
151
  elsif phrase =~ /\Afrom (.*)\z/
152
- from_phrase = $~[1]
152
+ from_phrase = $1
153
153
  to_phrase = nil
154
154
  elsif phrase =~ /\Ato (.*)\z/
155
- from_phrase = $~[1]
155
+ from_phrase = $1
156
156
  else
157
157
  from_phrase = phrase
158
158
  end
@@ -232,7 +232,7 @@ class Period
232
232
  when :year
233
233
  366
234
234
  when :irregular
235
- raise ArgumentError, "no maximum period for :irregular chunk"
235
+ raise ArgumentError, 'no maximum period for :irregular chunk'
236
236
  else
237
237
  chunk_sym_to_days(sym)
238
238
  end
@@ -273,16 +273,16 @@ class Period
273
273
 
274
274
  def to_s
275
275
  if first.beginning_of_year? && last.end_of_year? && first.year == last.year
276
- "#{first.year}"
276
+ first.year.to_s
277
277
  elsif first.beginning_of_quarter? &&
278
- last.end_of_quarter? &&
279
- first.year == last.year &&
280
- first.quarter == last.quarter
278
+ last.end_of_quarter? &&
279
+ first.year == last.year &&
280
+ first.quarter == last.quarter
281
281
  "#{first.year}-#{first.quarter}Q"
282
282
  elsif first.beginning_of_month? &&
283
- last.end_of_month? &&
284
- first.year == last.year &&
285
- first.month == last.month
283
+ last.end_of_month? &&
284
+ first.year == last.year &&
285
+ first.month == last.month
286
286
  "#{first.year}-%02d" % first.month
287
287
  else
288
288
  "#{first.iso} to #{last.iso}"
@@ -319,58 +319,54 @@ class Period
319
319
  to_range.proper_superset_of?(other.to_range)
320
320
  end
321
321
 
322
- def overlaps?(other)
323
- self.to_range.overlaps?(other.to_range)
324
- end
325
-
326
322
  def intersection(other)
327
- result = self.to_range.intersection(other.to_range)
323
+ result = to_range.intersection(other.to_range)
328
324
  if result.nil?
329
325
  nil
330
326
  else
331
327
  Period.new(result.first, result.last)
332
328
  end
333
329
  end
334
- alias_method :&, :intersection
335
- alias_method :narrow_to, :intersection
330
+ alias & intersection
331
+ alias narrow_to intersection
336
332
 
337
333
  def union(other)
338
- result = self.to_range.union(other.to_range)
334
+ result = to_range.union(other.to_range)
339
335
  Period.new(result.first, result.last)
340
336
  end
341
- alias_method :+, :union
337
+ alias + union
342
338
 
343
339
  def difference(other)
344
- ranges = self.to_range.difference(other.to_range)
345
- ranges.each.map{ |r| Period.new(r.first, r.last) }
340
+ ranges = to_range.difference(other.to_range)
341
+ ranges.each.map { |r| Period.new(r.first, r.last) }
346
342
  end
347
- alias_method :-, :difference
343
+ alias - difference
348
344
 
349
345
  # returns the chunk sym represented by the period
350
346
  def chunk_sym
351
347
  if first.beginning_of_year? && last.end_of_year? &&
352
- (365..366) === last - first + 1
348
+ (365..366) === last - first + 1
353
349
  :year
354
350
  elsif first.beginning_of_half? && last.end_of_half? &&
355
- (180..183) === last - first + 1
351
+ (180..183) === last - first + 1
356
352
  :half
357
353
  elsif first.beginning_of_quarter? && last.end_of_quarter? &&
358
- (90..92) === last - first + 1
354
+ (90..92) === last - first + 1
359
355
  :quarter
360
356
  elsif first.beginning_of_bimonth? && last.end_of_bimonth? &&
361
- (58..62) === last - first + 1
357
+ (58..62) === last - first + 1
362
358
  :bimonth
363
359
  elsif first.beginning_of_month? && last.end_of_month? &&
364
- (28..31) === last - first + 1
360
+ (28..31) === last - first + 1
365
361
  :month
366
362
  elsif first.beginning_of_semimonth? && last.end_of_semimonth &&
367
- (13..16) === last - first + 1
363
+ (13..16) === last - first + 1
368
364
  :semimonth
369
365
  elsif first.beginning_of_biweek? && last.end_of_biweek? &&
370
- last - first + 1 == 14
366
+ last - first + 1 == 14
371
367
  :biweek
372
368
  elsif first.beginning_of_week? && last.end_of_week? &&
373
- last - first + 1 == 7
369
+ last - first + 1 == 7
374
370
  :week
375
371
  elsif first == last
376
372
  :day
@@ -410,32 +406,28 @@ class Period
410
406
  end
411
407
 
412
408
  def contains?(date)
413
- if date.respond_to?(:to_date)
414
- date = date.to_date
415
- end
416
- unless (date.is_a? Date)
417
- raise ArgumentError, "argument must be a Date"
418
- end
419
- self.to_range.cover?(date)
409
+ date = date.to_date if date.respond_to?(:to_date)
410
+ raise ArgumentError, 'argument must be a Date' unless date.is_a?(Date)
411
+ to_range.cover?(date)
420
412
  end
421
413
 
422
414
  def overlaps?(other)
423
- self.to_range.overlaps?(other.to_range)
415
+ to_range.overlaps?(other.to_range)
424
416
  end
425
417
 
426
418
  # Return whether any of the Periods that are within self overlap one
427
419
  # another
428
420
  def has_overlaps_within?(periods)
429
- self.to_range.has_overlaps_within?(periods.map{ |p| p.to_range})
421
+ to_range.has_overlaps_within?(periods.map(&:to_range))
430
422
  end
431
423
 
432
424
  def spanned_by?(periods)
433
- to_range.spanned_by?(periods.map { |p| p.to_range })
425
+ to_range.spanned_by?(periods.map(&:to_range))
434
426
  end
435
427
 
436
428
  def gaps(periods)
437
- to_range.gaps(periods.map { |p| p.to_range }).
438
- map { |r| Period.new(r.first, r.last)}
429
+ to_range.gaps(periods.map(&:to_range))
430
+ .map { |r| Period.new(r.first, r.last) }
439
431
  end
440
432
 
441
433
  # Return an array of Periods wholly-contained within self in chunks of
@@ -444,7 +436,8 @@ class Period
444
436
  # respectively, are set true. The last chunk can be made to extend beyond
445
437
  # the end of self to make it a whole chunk if round_up_last is set true,
446
438
  # in which case, partial_last is ignored.
447
- def chunks(size: :month, partial_first: false, partial_last: false, round_up_last: false)
439
+ def chunks(size: :month, partial_first: false, partial_last: false,
440
+ round_up_last: false)
448
441
  size = size.to_sym
449
442
  result = []
450
443
  chunk_start = first.dup
@@ -467,27 +460,27 @@ class Period
467
460
  chunk_end = chunk_start.end_of_quarter
468
461
  when :bimonth
469
462
  unless partial_first
470
- chunk_start += 1.day until chunk_start.beginning_of_bimonth?
463
+ chunk_start += 1.day until chunk_start.beginning_of_bimonth?
471
464
  end
472
465
  chunk_end = (chunk_start.end_of_month + 1.day).end_of_month
473
466
  when :month
474
467
  unless partial_first
475
- chunk_start += 1.day until chunk_start.beginning_of_month?
468
+ chunk_start += 1.day until chunk_start.beginning_of_month?
476
469
  end
477
470
  chunk_end = chunk_start.end_of_month
478
471
  when :semimonth
479
472
  unless partial_first
480
- chunk_start += 1.day until chunk_start.beginning_of_semimonth?
473
+ chunk_start += 1.day until chunk_start.beginning_of_semimonth?
481
474
  end
482
475
  chunk_end = chunk_start.end_of_semimonth
483
476
  when :biweek
484
477
  unless partial_first
485
- chunk_start += 1.day until chunk_start.beginning_of_biweek?
478
+ chunk_start += 1.day until chunk_start.beginning_of_biweek?
486
479
  end
487
480
  chunk_end = chunk_start.end_of_biweek
488
481
  when :week
489
482
  unless partial_first
490
- chunk_start += 1.day until chunk_start.beginning_of_week?
483
+ chunk_start += 1.day until chunk_start.beginning_of_week?
491
484
  end
492
485
  chunk_end = chunk_start.end_of_week
493
486
  when :day
@@ -497,14 +490,12 @@ class Period
497
490
  end
498
491
  if chunk_end <= last
499
492
  result << Period.new(chunk_start, chunk_end)
493
+ elsif round_up_last
494
+ result << Period.new(chunk_start, chunk_end)
495
+ elsif partial_last
496
+ result << Period.new(chunk_start, last)
500
497
  else
501
- if round_up_last
502
- result << Period.new(chunk_start, chunk_end)
503
- elsif partial_last
504
- result << Period.new(chunk_start, last)
505
- else
506
- break
507
- end
498
+ break
508
499
  end
509
500
  chunk_start = result.last.last + 1.day
510
501
  end