Class: Delay

Inherits:
Object
  • Object
show all
Defined in:
lib/delay.rb

Overview

Delay permits to define explicit and complex delays Delay are not always linears due to BOM/EOM, so if D3 = D1 + D2 is true, D1 = D2 - D3 is not always true.

Defined Under Namespace

Modules: Validation

Constant Summary collapse

SEPARATOR =
','.freeze
TRANSLATIONS =
{
  fra: {
    'an' => :year,
    'ans' => :year,
    'année' => :year,
    'années' => :year,
    'annee' => :year,
    'annees' => :year,
    'mois' => :month,
    'semaine' => :week,
    'semaines' => :week,
    'jour' => :day,
    'jours' => :day,
    'heure' => :hour,
    'heures' => :hour,
    'minute' => :minute,
    'minutes' => :minute,
    'seconde' => :second,
    'secondes' => :second
  },
  eng: {
    'year' => :year,
    'years' => :year,
    'month' => :month,
    'months' => :month,
    'week' => :week,
    'weeks' => :week,
    'day' => :day,
    'days' => :day,
    'hour' => :hour,
    'hours' => :hour,
    'minute' => :minute,
    'minutes' => :minute,
    'second' => :second,
    'seconds' => :second,
  }
}.freeze
KEYS =
TRANSLATIONS.values.reduce(&:merge).keys.join('|').freeze
ALL_TRANSLATIONS =
TRANSLATIONS.values.reduce(&:merge)
MONTH_KEYWORDS =
{
  bom: {
    eng: ['bom', 'beginning of month'],
    fra: ['ddm', 'début du mois'] },
  eom: {
    eng: ['eom', 'end of month'],
    fra: ['fdm', 'fin du mois'] }
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(expression = nil) ⇒ Delay

Returns a new instance of Delay.


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/delay.rb', line 58

def initialize(expression = nil)
  base = (expression.nil? ? nil : expression.dup)
  expression ||= []
  expression = expression.to_s.strip.split(/\s*\,\s*/) if expression.is_a?(String)
  unless expression.is_a?(Array)
    raise ArgumentError, "String or Array expected (got #{expression.class.name}:#{expression.inspect})"
  end
  @expression = expression.collect do |step|
    # step = step.mb_chars.downcase
    if step =~ /\A(#{MONTH_KEYWORDS[:eom].values.flatten.join('|')})\z/
      [:eom]
    elsif step =~ /\A(#{MONTH_KEYWORDS[:bom].values.flatten.join('|')})\z/
      [:bom]
    elsif step =~ /\A\d+\ (#{KEYS})(\ (avant|ago))?\z/
      words = step.split(/\s+/).map(&:to_s)
      if ALL_TRANSLATIONS[words[1]].nil?
        raise InvalidDelayExpression, "#{words[1].inspect} is an undefined period (#{step.inspect} of #{base.inspect})"
      end
      [ALL_TRANSLATIONS[words[1]], (words[2].blank? ? 1 : -1) * words[0].to_i]
    elsif step.present?
      raise InvalidDelayExpression, "#{step.inspect} is an invalid step. (From #{base.inspect} => #{expression.inspect})"
    end
  end
end

Instance Attribute Details

#expressionObject (readonly)

Returns the value of attribute expression


56
57
58
# File 'lib/delay.rb', line 56

def expression
  @expression
end

Instance Method Details

#+(delay) ⇒ Object

Sums delays


133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/delay.rb', line 133

def +(delay)
  if delay.is_a?(Delay)
    Delay.new(to_s + ', ' + delay.to_s)
  elsif delay.is_a?(String)
    Delay.new(to_s + ', ' + Delay.new(delay).to_s)
  elsif delay.is_a?(Numeric)
    Delay.new(to_s + ', ' + Delay.new(delay.to_s + ' seconds').to_s)
  elsif delay.is_a?(Measure) && delay.dimension == :time && %i[second minute hour day month year].include?(delay.unit.to_sym)
    Delay.new(to_s + ', ' + Delay.new(delay.value.to_i.to_s + ' ' + delay.unit.to_s).to_s)
  else
    raise ArgumentError, "Cannot sum #{delay} [#{delay.class.name}] to a #{self.class.name}"
  end
end

#-(delay) ⇒ Object

Adds opposites values of given delay


148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/delay.rb', line 148

def -(delay)
  if delay.is_a?(Delay)
    Delay.new(to_s + ', ' + delay.invert.to_s)
  elsif delay.is_a?(String)
    Delay.new(to_s + ', ' + Delay.new(delay).invert.to_s)
  elsif delay.is_a?(Numeric)
    Delay.new(to_s + ', ' + Delay.new(delay.to_s + ' seconds').invert.to_s)
  elsif delay.is_a?(Measure) && delay.dimension == :time && %i[second minute hour day month year].include?(delay.unit.to_sym)
    Delay.new(to_s + ', ' + Delay.new(delay.value.to_i.to_s + ' ' + delay.unit.to_s).invert.to_s)
  else
    raise ArgumentError, "Cannot subtract #{delay} [#{delay.class.name}] from a #{self.class.name}"
  end
end

#compute(started_at = Time.zone.now) ⇒ Object


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/delay.rb', line 83

def compute(started_at = Time.zone.now)
  return nil if started_at.nil?
  stopped_at = started_at.dup
  @expression.each do |step|
    case step[0]
    when :eom
      stopped_at = stopped_at.end_of_month
    when :bom
      stopped_at = stopped_at.beginning_of_month
    else
      stopped_at += step[1].send(step[0])
    end
  end
  stopped_at
end

#inspectObject


99
100
101
102
103
104
# File 'lib/delay.rb', line 99

def inspect
  @expression.collect do |step|
    next step.first.to_s.upcase if step.size == 1
    step[1].abs.to_s + ' ' + step[0].to_s + 's' + (step[1] < 0 ? ' ago' : '')
  end.join(', ')
end

#invertObject

Return a duplicated inverted copy


128
129
130
# File 'lib/delay.rb', line 128

def invert
  dup.invert!
end

#invert!Object

Invert steps :

* EOM -> BOM
* BOM -> EOM
* x <duration> -> x <duration> ago

114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/delay.rb', line 114

def invert!
  @expression = @expression.collect do |step|
    if step.first == :eom
      [:bom]
    elsif step.first == :bom
      [:eom]
    else
      [step.first, -step.second]
    end
  end
  self
end

#to_sObject


106
107
108
# File 'lib/delay.rb', line 106

def to_s
  inspect
end