Class: SOF::Cycle
- Inherits:
-
Object
- Object
- SOF::Cycle
- Extended by:
- Forwardable
- Defined in:
- lib/sof/cycle.rb,
lib/sof/cycle/version.rb
Direct Known Subclasses
SOF::Cycles::Calendar, SOF::Cycles::EndOf, SOF::Cycles::Lookback, SOF::Cycles::VolumeOnly, SOF::Cycles::Within
Defined Under Namespace
Classes: InvalidInput, InvalidKind, InvalidPeriod
Constant Summary collapse
- VERSION =
"0.1.6"
Class Attribute Summary collapse
-
.kind ⇒ Object
readonly
Returns the value of attribute kind.
-
.notation_id ⇒ Object
readonly
Returns the value of attribute notation_id.
-
.valid_periods ⇒ Object
readonly
Returns the value of attribute valid_periods.
Instance Attribute Summary collapse
-
#parser ⇒ Object
readonly
Returns the value of attribute parser.
Class Method Summary collapse
-
.class_for_kind(sym) ⇒ Object
Return the class handling the kind.
-
.class_for_notation_id(notation_id) ⇒ Object
Return the appropriate class for the give notation id.
- .cycle_handlers ⇒ Object
-
.dump(cycle_or_string) ⇒ Object
Turn a cycle or notation string into a hash.
-
.for(notation) ⇒ Cycle
Return a Cycle object from a notation string.
- .handles?(sym) ⇒ Boolean
- .inherited(klass) ⇒ Object
-
.load(hash) ⇒ Object
Return a Cycle object from a hash.
-
.notation(hash) ⇒ String
Retun a notation string from a hash.
- .recurring? ⇒ Boolean
-
.validate_period(period) ⇒ Object
Raises an error if the given period isn’t in the list of valid periods.
- .volume_only? ⇒ Boolean
Instance Method Summary collapse
-
#==(other) ⇒ Object
Cycles are considered equal if their hash representations are equal.
- #as_json ⇒ Object
- #cover?(date, anchor: Date.current) ⇒ Boolean
- #covered_dates(dates, anchor: Date.current) ⇒ Object
- #expiration_of(_completion_dates, anchor: Date.current) ⇒ Object
- #extend_period(_ = nil) ⇒ Object
-
#final_date(_anchor) ⇒ Object
Return the final date of the cycle.
- #from_data ⇒ Object
- #humanized_span ⇒ Object
-
#initialize(notation, parser: Parser.new(notation)) ⇒ Cycle
constructor
A new instance of Cycle.
- #kind_inquiry ⇒ Object
-
#last_completed(dates) ⇒ Object
Return the most recent completion date from the supplied array of dates.
-
#notation ⇒ Object
Return the cycle representation as a notation string.
- #range(anchor) ⇒ Object
-
#satisfied_by?(completion_dates, anchor: Date.current) ⇒ Boolean
From the supplied anchor date, are there enough in-window completions to satisfy the cycle?.
- #to_h ⇒ Object
- #validate_period ⇒ Object
- #volume_to_delay_expiration(_completion_dates, anchor:) ⇒ Object
Constructor Details
#initialize(notation, parser: Parser.new(notation)) ⇒ Cycle
Returns a new instance of Cycle.
132 133 134 135 136 137 138 139 140 |
# File 'lib/sof/cycle.rb', line 132 def initialize(notation, parser: Parser.new(notation)) @notation = notation @parser = parser validate_period return if @parser.valid? raise InvalidInput, "'#{notation}' is not a valid input" end |
Class Attribute Details
.kind ⇒ Object (readonly)
Returns the value of attribute kind.
115 116 117 |
# File 'lib/sof/cycle.rb', line 115 def kind @kind end |
.notation_id ⇒ Object (readonly)
Returns the value of attribute notation_id.
115 116 117 |
# File 'lib/sof/cycle.rb', line 115 def notation_id @notation_id end |
.valid_periods ⇒ Object (readonly)
Returns the value of attribute valid_periods.
115 116 117 |
# File 'lib/sof/cycle.rb', line 115 def valid_periods @valid_periods end |
Instance Attribute Details
#parser ⇒ Object (readonly)
Returns the value of attribute parser.
142 143 144 |
# File 'lib/sof/cycle.rb', line 142 def parser @parser end |
Class Method Details
.class_for_kind(sym) ⇒ Object
Return the class handling the kind
96 97 98 99 100 |
# File 'lib/sof/cycle.rb', line 96 def class_for_kind(sym) Cycle.cycle_handlers.find do |klass| klass.handles?(sym) end || raise(InvalidKind, "':#{sym}' is not a valid kind of Cycle") end |
.class_for_notation_id(notation_id) ⇒ Object
Return the appropriate class for the give notation id
85 86 87 88 89 |
# File 'lib/sof/cycle.rb', line 85 def class_for_notation_id(notation_id) cycle_handlers.find do |klass| klass.notation_id == notation_id end || raise(InvalidKind, "'#{notation_id}' is not a valid kind of #{name}") end |
.cycle_handlers ⇒ Object
102 |
# File 'lib/sof/cycle.rb', line 102 def cycle_handlers = @cycle_handlers ||= Set.new |
.dump(cycle_or_string) ⇒ Object
Turn a cycle or notation string into a hash
16 17 18 19 20 21 22 |
# File 'lib/sof/cycle.rb', line 16 def dump(cycle_or_string) if cycle_or_string.is_a? Cycle cycle_or_string else Cycle.for(cycle_or_string) end.to_h end |
.for(notation) ⇒ Cycle
Return a Cycle object from a notation string
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/sof/cycle.rb', line 63 def for(notation) return notation if notation.is_a? Cycle return notation if notation.is_a? Cycles::Dormant parser = Parser.new(notation) unless parser.valid? raise InvalidInput, "'#{notation}' is not a valid input" end cycle = cycle_handlers.find do |klass| parser.parses?(klass.notation_id) end.new(notation, parser:) return cycle if parser.active? Cycles::Dormant.new(cycle, parser:) end |
.handles?(sym) ⇒ Boolean
106 107 108 |
# File 'lib/sof/cycle.rb', line 106 def handles?(sym) sym && kind == sym.to_sym end |
.inherited(klass) ⇒ Object
104 |
# File 'lib/sof/cycle.rb', line 104 def inherited(klass) = cycle_handlers << klass |
.load(hash) ⇒ Object
Return a Cycle object from a hash
25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/sof/cycle.rb', line 25 def load(hash) symbolized_hash = hash.symbolize_keys cycle_class = class_for_kind(symbolized_hash[:kind]) unless cycle_class.valid_periods.empty? cycle_class.validate_period( TimeSpan.notation_id_from_name(symbolized_hash[:period]) ) end Cycle.for notation(symbolized_hash) rescue TimeSpan::InvalidPeriod => exc raise InvalidPeriod, exc. end |
.notation(hash) ⇒ String
Retun a notation string from a hash
44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/sof/cycle.rb', line 44 def notation(hash) volume_notation = "V#{hash.fetch(:volume) { 1 }}" return volume_notation if hash[:kind].nil? || hash[:kind].to_sym == :volume_only cycle_class = class_for_kind(hash[:kind].to_sym) [ volume_notation, cycle_class.notation_id, TimeSpan.notation(hash.slice(:period, :period_count)), hash.fetch(:from, nil) ].compact.join end |
.recurring? ⇒ Boolean
118 |
# File 'lib/sof/cycle.rb', line 118 def recurring? = raise "#{name} must implement #{__method__}" |
.validate_period(period) ⇒ Object
Raises an error if the given period isn’t in the list of valid periods.
124 125 126 127 128 129 |
# File 'lib/sof/cycle.rb', line 124 def validate_period(period) raise InvalidPeriod, <<~ERR.squish unless valid_periods.include?(period) Invalid period value of '#{period}' provided. Valid periods are: #{valid_periods.join(", ")} ERR end |
.volume_only? ⇒ Boolean
116 |
# File 'lib/sof/cycle.rb', line 116 def volume_only? = @volume_only |
Instance Method Details
#==(other) ⇒ Object
Cycles are considered equal if their hash representations are equal
163 |
# File 'lib/sof/cycle.rb', line 163 def ==(other) = to_h == other.to_h |
#as_json ⇒ Object
215 |
# File 'lib/sof/cycle.rb', line 215 def as_json(...) = notation |
#cover?(date, anchor: Date.current) ⇒ Boolean
184 185 186 |
# File 'lib/sof/cycle.rb', line 184 def cover?(date, anchor: Date.current) range(anchor).cover?(date) end |
#covered_dates(dates, anchor: Date.current) ⇒ Object
178 179 180 181 182 |
# File 'lib/sof/cycle.rb', line 178 def covered_dates(dates, anchor: Date.current) dates.select do |date| cover?(date, anchor:) end end |
#expiration_of(_completion_dates, anchor: Date.current) ⇒ Object
195 |
# File 'lib/sof/cycle.rb', line 195 def expiration_of(_completion_dates, anchor: Date.current) = nil |
#extend_period(_ = nil) ⇒ Object
168 |
# File 'lib/sof/cycle.rb', line 168 def extend_period(_ = nil) = self |
#final_date(_anchor) ⇒ Object
Return the final date of the cycle
193 |
# File 'lib/sof/cycle.rb', line 193 def final_date(_anchor) = nil |
#from_data ⇒ Object
209 210 211 212 213 |
# File 'lib/sof/cycle.rb', line 209 def from_data return {} unless from {from: from} end |
#humanized_span ⇒ Object
190 |
# File 'lib/sof/cycle.rb', line 190 def humanized_span = [period_count, humanized_period].join(" ") |
#kind_inquiry ⇒ Object
151 |
# File 'lib/sof/cycle.rb', line 151 def kind_inquiry = ActiveSupport::StringInquirer.new(kind.to_s) |
#last_completed(dates) ⇒ Object
Return the most recent completion date from the supplied array of dates
166 |
# File 'lib/sof/cycle.rb', line 166 def last_completed(dates) = dates.compact.map(&:to_date).max |
#notation ⇒ Object
Return the cycle representation as a notation string
160 |
# File 'lib/sof/cycle.rb', line 160 def notation = self.class.notation(to_h) |
#range(anchor) ⇒ Object
188 |
# File 'lib/sof/cycle.rb', line 188 def range(anchor) = start_date(anchor)..final_date(anchor) |
#satisfied_by?(completion_dates, anchor: Date.current) ⇒ Boolean
From the supplied anchor date, are there enough in-window completions to satisfy the cycle?
174 175 176 |
# File 'lib/sof/cycle.rb', line 174 def satisfied_by?(completion_dates, anchor: Date.current) covered_dates(completion_dates, anchor:).size >= volume end |
#to_h ⇒ Object
199 200 201 202 203 204 205 206 207 |
# File 'lib/sof/cycle.rb', line 199 def to_h { kind:, volume:, period:, period_count:, **from_data } end |
#validate_period ⇒ Object
153 154 155 156 157 |
# File 'lib/sof/cycle.rb', line 153 def validate_period return if valid_periods.empty? self.class.validate_period(period_key) end |
#volume_to_delay_expiration(_completion_dates, anchor:) ⇒ Object
197 |
# File 'lib/sof/cycle.rb', line 197 def volume_to_delay_expiration(_completion_dates, anchor:) = 0 |