sof-cycle 0.1.13 → 0.1.15
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.
- checksums.yaml +4 -4
- data/.tool-versions +1 -1
- data/CHANGELOG.md +2 -18
- data/checksums/sof-cycle-0.1.13.gem.sha512 +1 -0
- data/checksums/sof-cycle-0.1.14.gem.sha512 +1 -0
- data/checksums/sof-cycle-0.1.3.gem.sha512 +1 -0
- data/checksums/sof-cycle-0.1.4.gem.sha512 +1 -0
- data/checksums/sof-cycle-0.1.5.gem.sha512 +1 -0
- data/lib/sof/cycle/parser.rb +127 -0
- data/lib/sof/cycle/registry.rb +86 -0
- data/lib/sof/cycle/time_span.rb +317 -0
- data/lib/sof/cycle/version.rb +1 -1
- data/lib/sof/cycle.rb +116 -31
- data/lib/sof/cycles/calendar.rb +1 -4
- data/lib/sof/cycles/dormant.rb +4 -0
- data/lib/sof/cycles/end_of.rb +1 -4
- data/lib/sof/cycles/interval.rb +1 -4
- data/lib/sof/cycles/lookback.rb +1 -4
- data/lib/sof/cycles/lookback_end_of.rb +40 -0
- data/lib/sof/cycles/volume_only.rb +1 -4
- data/lib/sof/cycles/within.rb +2 -5
- data/lib/sof-cycle.rb +1 -1
- metadata +11 -5
- data/.claude/settings.local.json +0 -17
- data/lib/sof/parser.rb +0 -109
- data/lib/sof/time_span.rb +0 -234
data/lib/sof/time_span.rb
DELETED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module SOF
|
|
4
|
-
# This class is not intended to be referenced directly.
|
|
5
|
-
# This is an internal implementation of Cycle behavior.
|
|
6
|
-
class TimeSpan
|
|
7
|
-
extend Forwardable
|
|
8
|
-
# TimeSpan objects map Cycle notations to behaviors for their periods
|
|
9
|
-
#
|
|
10
|
-
# For example:
|
|
11
|
-
# 'M' => TimeSpan::DatePeriod::Month
|
|
12
|
-
# 'Y' => TimeSpan::DatePeriod::Year
|
|
13
|
-
# Read each DatePeriod subclass for more information.
|
|
14
|
-
#
|
|
15
|
-
class InvalidPeriod < StandardError; end
|
|
16
|
-
|
|
17
|
-
class << self
|
|
18
|
-
# Return a time_span for the given count and period
|
|
19
|
-
def for(count, period)
|
|
20
|
-
case count.to_i
|
|
21
|
-
when 0
|
|
22
|
-
TimeSpanNothing
|
|
23
|
-
when 1
|
|
24
|
-
TimeSpanOne
|
|
25
|
-
else
|
|
26
|
-
self
|
|
27
|
-
end.new(count, period)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Return a notation string from a hash
|
|
31
|
-
def notation(hash)
|
|
32
|
-
return unless hash.key?(:period) && hash[:period].present?
|
|
33
|
-
|
|
34
|
-
[
|
|
35
|
-
hash.fetch(:period_count) { 1 },
|
|
36
|
-
notation_id_from_name(hash[:period])
|
|
37
|
-
].compact.join
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Return the notation character for the given period name
|
|
41
|
-
def notation_id_from_name(name)
|
|
42
|
-
type = DatePeriod.types.find do |klass|
|
|
43
|
-
klass.period.to_s == name.to_s
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
raise InvalidPeriod, "'#{name}' is not a valid period" unless type
|
|
47
|
-
|
|
48
|
-
type.code
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def notation
|
|
53
|
-
[period_count, code].join
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Class used to calculate the windows of time so that
|
|
57
|
-
# a TimeSpan object will know the correct end of year,
|
|
58
|
-
# quarter, etc.
|
|
59
|
-
class DatePeriod
|
|
60
|
-
extend Forwardable
|
|
61
|
-
class << self
|
|
62
|
-
def for(count, period_notation)
|
|
63
|
-
@cached_periods ||= {}
|
|
64
|
-
@cached_periods[period_notation] ||= {}
|
|
65
|
-
@cached_periods[period_notation][count] ||= (for_notation(period_notation) || self).new(count)
|
|
66
|
-
@cached_periods[period_notation][count]
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def for_notation(notation)
|
|
70
|
-
DatePeriod.types.find do |klass|
|
|
71
|
-
klass.code == notation.to_s.upcase
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def types = @types ||= Set.new
|
|
76
|
-
|
|
77
|
-
def inherited(klass)
|
|
78
|
-
DatePeriod.types << klass
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
@period = nil
|
|
82
|
-
@code = nil
|
|
83
|
-
@interval = nil
|
|
84
|
-
attr_reader :period, :code, :interval
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
delegate [:period, :code, :interval] => "self.class"
|
|
88
|
-
|
|
89
|
-
def initialize(count)
|
|
90
|
-
@count = count
|
|
91
|
-
end
|
|
92
|
-
attr_reader :count
|
|
93
|
-
|
|
94
|
-
def end_date(date)
|
|
95
|
-
@end_date ||= {}
|
|
96
|
-
@end_date[date] ||= date + duration
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def begin_date(date)
|
|
100
|
-
@begin_date ||= {}
|
|
101
|
-
@begin_date[date] ||= date - duration
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def duration = count.send(period)
|
|
105
|
-
|
|
106
|
-
def end_of_period(_) = nil
|
|
107
|
-
|
|
108
|
-
def humanized_period
|
|
109
|
-
return period if count == 1
|
|
110
|
-
|
|
111
|
-
"#{period}s"
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
class Year < self
|
|
115
|
-
@period = :year
|
|
116
|
-
@code = "Y"
|
|
117
|
-
@interval = "years"
|
|
118
|
-
|
|
119
|
-
def end_of_period(date)
|
|
120
|
-
date.end_of_year
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def beginning_of_period(date)
|
|
124
|
-
date.beginning_of_year
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
class Quarter < self
|
|
129
|
-
@period = :quarter
|
|
130
|
-
@code = "Q"
|
|
131
|
-
@interval = "quarters"
|
|
132
|
-
|
|
133
|
-
def duration
|
|
134
|
-
(count * 3).months
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def end_of_period(date)
|
|
138
|
-
date.end_of_quarter
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def beginning_of_period(date)
|
|
142
|
-
date.beginning_of_quarter
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
class Month < self
|
|
147
|
-
@period = :month
|
|
148
|
-
@code = "M"
|
|
149
|
-
@interval = "months"
|
|
150
|
-
|
|
151
|
-
def end_of_period(date)
|
|
152
|
-
date.end_of_month
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
def beginning_of_period(date)
|
|
156
|
-
date.beginning_of_month
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
class Week < self
|
|
161
|
-
@period = :week
|
|
162
|
-
@code = "W"
|
|
163
|
-
@interval = "weeks"
|
|
164
|
-
|
|
165
|
-
def end_of_period(date)
|
|
166
|
-
date.end_of_week
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def beginning_of_period(date)
|
|
170
|
-
date.beginning_of_week
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
class Day < self
|
|
175
|
-
@period = :day
|
|
176
|
-
@code = "D"
|
|
177
|
-
@interval = "days"
|
|
178
|
-
|
|
179
|
-
def end_of_period(date)
|
|
180
|
-
date
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
def beginning_of_period(date)
|
|
184
|
-
date
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
private_constant :DatePeriod
|
|
189
|
-
|
|
190
|
-
def initialize(count, period_id)
|
|
191
|
-
@count = Integer(count, exception: false)
|
|
192
|
-
@window = DatePeriod.for(period_count, period_id)
|
|
193
|
-
end
|
|
194
|
-
attr_reader :window
|
|
195
|
-
|
|
196
|
-
delegate [:end_date, :begin_date, :code] => :window
|
|
197
|
-
|
|
198
|
-
def end_date_of_period(date)
|
|
199
|
-
window.end_of_period(date)
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def begin_date_of_period(date)
|
|
203
|
-
window.beginning_of_period(date)
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
# Integer value for the period count or nil
|
|
207
|
-
def period_count
|
|
208
|
-
@count
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
delegate [:period, :duration, :interval, :humanized_period] => :window
|
|
212
|
-
|
|
213
|
-
# Return a date according to the rules of the time_span
|
|
214
|
-
def final_date(date)
|
|
215
|
-
return unless period
|
|
216
|
-
|
|
217
|
-
window.end_date(date.to_date)
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def to_h
|
|
221
|
-
{
|
|
222
|
-
period:,
|
|
223
|
-
period_count:
|
|
224
|
-
}
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
class TimeSpanNothing < self
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
class TimeSpanOne < self
|
|
231
|
-
def interval = humanized_period
|
|
232
|
-
end
|
|
233
|
-
end
|
|
234
|
-
end
|