trifle-stats 2.6.0 → 2.7.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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/lib/trifle/stats/driver/api.rb +45 -26
- data/lib/trifle/stats/nocturnal.rb +151 -78
- data/lib/trifle/stats/operations/timeseries/increment.rb +23 -15
- data/lib/trifle/stats/operations/timeseries/set.rb +23 -15
- data/lib/trifle/stats/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70c1c7a45dc8833c358ebab3d93a55e15f1f42f4459eb0d5fab14b7383de0a04
|
|
4
|
+
data.tar.gz: c09506de6ef9cd69b35733a616d43a09aa62ad8c6013f32552d1a0b7dcf870f6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c980da761d8029b971ad9520d898d1971ba164c7cb2a1d7ffdbad60c67820cbb7eecf0ca25fa547518f06615dbe71411f376e20f2672dac1c9fca5c5dddaa5aa
|
|
7
|
+
data.tar.gz: 23266f716645654a5b642abf1558bbc85667b069dfb1caa98cfa1cc0899fd732769d2e0628107d06397b9b50996630b24a8f4ad6b634c5a8851d9984a66c7ad0
|
data/Gemfile.lock
CHANGED
|
@@ -60,32 +60,8 @@ module Trifle
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def direct_write(operation:, key:, at:, values:, untracked: false)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
key: key,
|
|
66
|
-
at: at.to_time.iso8601(6),
|
|
67
|
-
values: values,
|
|
68
|
-
untracked: untracked
|
|
69
|
-
))
|
|
70
|
-
request = Net::HTTP::Post.new(ENDPOINT)
|
|
71
|
-
request['Authorization'] = "Bearer #{token}"
|
|
72
|
-
request['X-Trifle-Source-Id'] = project_id
|
|
73
|
-
request['Content-Type'] = 'application/json'
|
|
74
|
-
request['Accept'] = 'application/json'
|
|
75
|
-
request['Content-Encoding'] = 'gzip'
|
|
76
|
-
request['User-Agent'] = "trifle-stats-ruby/#{Trifle::Stats::VERSION}"
|
|
77
|
-
request.body = body
|
|
78
|
-
|
|
79
|
-
response = @transport.call(uri: ENDPOINT, request: request, timeout: TIMEOUT)
|
|
80
|
-
return true if response.code.to_i.between?(200, 299)
|
|
81
|
-
|
|
82
|
-
response_body = response.body.to_s.byteslice(0, ERROR_BODY_LIMIT)
|
|
83
|
-
raise Error.new(
|
|
84
|
-
"Trifle API returned HTTP #{response.code}",
|
|
85
|
-
status: response.code.to_i,
|
|
86
|
-
response_body: response_body,
|
|
87
|
-
retry_after: response['Retry-After']
|
|
88
|
-
)
|
|
63
|
+
payload = api_payload(operation, key, at, values, untracked)
|
|
64
|
+
deliver(build_request(payload))
|
|
89
65
|
rescue Error
|
|
90
66
|
raise
|
|
91
67
|
rescue StandardError => e
|
|
@@ -114,6 +90,49 @@ module Trifle
|
|
|
114
90
|
|
|
115
91
|
private
|
|
116
92
|
|
|
93
|
+
def api_payload(operation, key, at, values, untracked)
|
|
94
|
+
{
|
|
95
|
+
operation: operation.to_s,
|
|
96
|
+
key: key,
|
|
97
|
+
at: at.to_time.iso8601(6),
|
|
98
|
+
values: values,
|
|
99
|
+
untracked: untracked
|
|
100
|
+
}
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def build_request(payload)
|
|
104
|
+
request = Net::HTTP::Post.new(ENDPOINT, request_headers)
|
|
105
|
+
request.body = gzip(JSON.generate(payload))
|
|
106
|
+
request
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def request_headers
|
|
110
|
+
{
|
|
111
|
+
'Authorization' => "Bearer #{token}",
|
|
112
|
+
'X-Trifle-Source-Id' => project_id,
|
|
113
|
+
'Content-Type' => 'application/json',
|
|
114
|
+
'Accept' => 'application/json',
|
|
115
|
+
'Content-Encoding' => 'gzip',
|
|
116
|
+
'User-Agent' => "trifle-stats-ruby/#{Trifle::Stats::VERSION}"
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def deliver(request)
|
|
121
|
+
response = @transport.call(uri: ENDPOINT, request: request, timeout: TIMEOUT)
|
|
122
|
+
return true if response.code.to_i.between?(200, 299)
|
|
123
|
+
|
|
124
|
+
raise_response_error(response)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def raise_response_error(response)
|
|
128
|
+
raise Error.new(
|
|
129
|
+
"Trifle API returned HTTP #{response.code}",
|
|
130
|
+
status: response.code.to_i,
|
|
131
|
+
response_body: response.body.to_s.byteslice(0, ERROR_BODY_LIMIT),
|
|
132
|
+
retry_after: response['Retry-After']
|
|
133
|
+
)
|
|
134
|
+
end
|
|
135
|
+
|
|
117
136
|
def gzip(value)
|
|
118
137
|
output = StringIO.new
|
|
119
138
|
Zlib::GzipWriter.wrap(output) { |writer| writer.write(value) }
|
|
@@ -26,7 +26,8 @@ module Trifle
|
|
|
26
26
|
item = from.dup
|
|
27
27
|
while item <= to
|
|
28
28
|
list << item
|
|
29
|
-
|
|
29
|
+
candidate = new(item, config: config).add(offset, unit)
|
|
30
|
+
item = new(candidate, config: config).floor(offset, unit)
|
|
30
31
|
end
|
|
31
32
|
list
|
|
32
33
|
end
|
|
@@ -49,53 +50,21 @@ module Trifle
|
|
|
49
50
|
|
|
50
51
|
case unit
|
|
51
52
|
when :second
|
|
52
|
-
|
|
53
|
+
add_elapsed(offset)
|
|
53
54
|
when :minute
|
|
54
|
-
|
|
55
|
+
add_elapsed(offset * 60)
|
|
55
56
|
when :hour
|
|
56
|
-
|
|
57
|
+
add_elapsed(offset * 3600)
|
|
57
58
|
when :day
|
|
58
|
-
|
|
59
|
+
add_days(calendar_offset(offset))
|
|
59
60
|
when :week
|
|
60
|
-
|
|
61
|
+
add_days(calendar_offset(offset) * 7)
|
|
61
62
|
when :month
|
|
62
|
-
|
|
63
|
-
result = time
|
|
64
|
-
offset.times do
|
|
65
|
-
result = add_one_month(result)
|
|
66
|
-
end
|
|
67
|
-
result
|
|
63
|
+
add_months(calendar_offset(offset))
|
|
68
64
|
when :quarter
|
|
69
|
-
|
|
70
|
-
result = time
|
|
71
|
-
(offset * 3).times do
|
|
72
|
-
result = add_one_month(result)
|
|
73
|
-
end
|
|
74
|
-
result
|
|
65
|
+
add_months(calendar_offset(offset) * 3)
|
|
75
66
|
when :year
|
|
76
|
-
|
|
77
|
-
begin
|
|
78
|
-
Time.new(
|
|
79
|
-
time.year + offset,
|
|
80
|
-
time.month,
|
|
81
|
-
time.day,
|
|
82
|
-
time.hour,
|
|
83
|
-
time.min,
|
|
84
|
-
time.sec,
|
|
85
|
-
config.tz.utc_offset
|
|
86
|
-
)
|
|
87
|
-
rescue ArgumentError
|
|
88
|
-
# Handle edge cases like Feb 29 in non-leap years
|
|
89
|
-
Time.new(
|
|
90
|
-
time.year + offset,
|
|
91
|
-
time.month,
|
|
92
|
-
[time.day, days_in_month(time.year + offset, time.month)].min,
|
|
93
|
-
time.hour,
|
|
94
|
-
time.min,
|
|
95
|
-
time.sec,
|
|
96
|
-
time.utc_offset
|
|
97
|
-
)
|
|
98
|
-
end
|
|
67
|
+
add_years(calendar_offset(offset))
|
|
99
68
|
end
|
|
100
69
|
end
|
|
101
70
|
|
|
@@ -104,89 +73,193 @@ module Trifle
|
|
|
104
73
|
raise ArgumentError, 'Segment size must be positive' unless offset.positive?
|
|
105
74
|
raise ArgumentError, "Invalid unit: #{unit}" unless Trifle::Stats::Nocturnal::UNIT_MAP.values.include?(unit)
|
|
106
75
|
|
|
76
|
+
source = localized_time
|
|
77
|
+
|
|
107
78
|
case unit
|
|
108
79
|
when :second
|
|
109
80
|
# Floor to second segment boundary
|
|
110
|
-
total_seconds =
|
|
81
|
+
total_seconds = source.sec
|
|
111
82
|
floored_seconds = (total_seconds / offset) * offset
|
|
112
|
-
|
|
83
|
+
subtract_elapsed(source, (total_seconds - floored_seconds) + source.subsec)
|
|
113
84
|
|
|
114
85
|
when :minute
|
|
115
86
|
# Floor to minute segment boundary (segments start from beginning of hour)
|
|
116
|
-
minutes_from_hour_start =
|
|
87
|
+
minutes_from_hour_start = source.min
|
|
117
88
|
floored_minutes = (minutes_from_hour_start / offset) * offset
|
|
118
|
-
|
|
89
|
+
seconds = ((minutes_from_hour_start - floored_minutes) * 60) + source.sec + source.subsec
|
|
90
|
+
subtract_elapsed(source, seconds)
|
|
119
91
|
|
|
120
92
|
when :hour
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
93
|
+
# Hour segments are elapsed durations anchored at local day start.
|
|
94
|
+
day_start = local_midnight(source.to_date, source)
|
|
95
|
+
segment = offset * 3600
|
|
96
|
+
elapsed = source - day_start
|
|
97
|
+
add_elapsed_to(day_start, (elapsed / segment).floor * segment)
|
|
125
98
|
|
|
126
99
|
when :day
|
|
127
100
|
# Floor to day segment boundary (segments start from beginning of year)
|
|
128
|
-
days_from_year_start =
|
|
101
|
+
days_from_year_start = source.yday - 1 # yday is 1-indexed, we want 0-indexed
|
|
129
102
|
floored_days = (days_from_year_start / offset) * offset
|
|
130
|
-
|
|
131
|
-
result_date
|
|
132
|
-
Time.new(result_date.year, result_date.month, result_date.day, 0, 0, 0, time.utc_offset)
|
|
103
|
+
result_date = Date.new(source.year, 1, 1) + floored_days
|
|
104
|
+
local_midnight(result_date, source)
|
|
133
105
|
|
|
134
106
|
when :week
|
|
135
107
|
# Floor to week segment boundary (segments start from beginning of year)
|
|
136
|
-
|
|
108
|
+
year_start_date = Date.new(source.year, 1, 1)
|
|
137
109
|
|
|
138
110
|
# Find the first week boundary of the year based on week_start
|
|
139
|
-
week_start_offset = DAYS_INTO_WEEK.fetch(
|
|
140
|
-
|
|
141
|
-
)
|
|
142
|
-
year_start_wday = year_start.wday
|
|
111
|
+
week_start_offset = DAYS_INTO_WEEK.fetch(config.beginning_of_week)
|
|
112
|
+
year_start_wday = year_start_date.wday
|
|
143
113
|
days_to_first_week_start = (week_start_offset - year_start_wday) % 7
|
|
144
|
-
first_week_start =
|
|
145
|
-
first_week_start_time = Time.new(first_week_start.year, first_week_start.month, first_week_start.day, 0, 0, 0, time.utc_offset) # rubocop:disable Layout/LineLength
|
|
114
|
+
first_week_start = year_start_date + days_to_first_week_start
|
|
146
115
|
|
|
147
116
|
# If current time is before first week boundary, use year start
|
|
148
|
-
if
|
|
149
|
-
|
|
117
|
+
if source.to_date < first_week_start
|
|
118
|
+
local_midnight(year_start_date, source)
|
|
150
119
|
else
|
|
151
|
-
|
|
152
|
-
weeks_since_first = ((time - first_week_start_time) / (7 * 86_400)).to_i
|
|
120
|
+
weeks_since_first = ((source.to_date - first_week_start).to_i / 7)
|
|
153
121
|
floored_weeks = (weeks_since_first / offset) * offset
|
|
154
122
|
|
|
155
123
|
result_date = first_week_start + (floored_weeks * 7)
|
|
156
|
-
|
|
124
|
+
local_midnight(result_date, source)
|
|
157
125
|
end
|
|
158
126
|
|
|
159
127
|
when :month
|
|
160
128
|
# Floor to month segment boundary (from start of year)
|
|
161
|
-
months_from_jan =
|
|
129
|
+
months_from_jan = source.month - 1 # 0-indexed
|
|
162
130
|
floored_months = (months_from_jan / offset) * offset
|
|
163
|
-
|
|
131
|
+
date = Date.new(source.year, floored_months + 1, 1)
|
|
132
|
+
local_midnight(date, source)
|
|
164
133
|
|
|
165
134
|
when :quarter
|
|
166
135
|
# Floor to quarter segment boundary
|
|
167
|
-
current_quarter = ((
|
|
136
|
+
current_quarter = ((source.month - 1) / 3) # 0-indexed quarters
|
|
168
137
|
floored_quarters = (current_quarter / offset) * offset
|
|
169
138
|
quarter_start_month = (floored_quarters * 3) + 1
|
|
170
|
-
|
|
139
|
+
date = Date.new(source.year, quarter_start_month, 1)
|
|
140
|
+
local_midnight(date, source)
|
|
171
141
|
|
|
172
142
|
when :year
|
|
173
143
|
# Floor to year segment boundary
|
|
174
|
-
floored_years = (
|
|
175
|
-
|
|
144
|
+
floored_years = (source.year / offset) * offset
|
|
145
|
+
date = Date.new(floored_years, 1, 1)
|
|
146
|
+
local_midnight(date, source)
|
|
176
147
|
end
|
|
177
148
|
end
|
|
178
149
|
|
|
179
150
|
private
|
|
180
151
|
|
|
181
|
-
def
|
|
182
|
-
|
|
183
|
-
|
|
152
|
+
def add_elapsed(seconds)
|
|
153
|
+
add_elapsed_to(time, seconds)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def add_elapsed_to(source, seconds)
|
|
157
|
+
config.tz.utc_to_local((source + seconds).getutc)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def subtract_elapsed(source, seconds)
|
|
161
|
+
config.tz.utc_to_local((source - seconds).getutc)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def add_days(days)
|
|
165
|
+
source = localized_time
|
|
166
|
+
local_time_for_source(source.to_date + days, source)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def add_months(months)
|
|
170
|
+
source = localized_time
|
|
171
|
+
total_months = (source.year * 12) + (source.month - 1) + months
|
|
172
|
+
year, month_index = total_months.divmod(12)
|
|
173
|
+
month = month_index + 1
|
|
174
|
+
day = [source.day, days_in_month(year, month)].min
|
|
175
|
+
|
|
176
|
+
local_time_for_source(Date.new(year, month, day), source)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def add_years(years)
|
|
180
|
+
source = localized_time
|
|
181
|
+
year = source.year + years
|
|
182
|
+
day = [source.day, days_in_month(year, source.month)].min
|
|
183
|
+
|
|
184
|
+
local_time_for_source(Date.new(year, source.month, day), source)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def localized_time
|
|
188
|
+
config.tz.utc_to_local(time.getutc)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def calendar_offset(offset)
|
|
192
|
+
return offset if offset.is_a?(Integer)
|
|
193
|
+
|
|
194
|
+
raise ArgumentError, 'Offset must be an integer for calendar units'
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def local_midnight(date, source)
|
|
198
|
+
local_time(date, source: source, hour: 0, minute: 0, second: 0)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def local_time_for_source(date, source)
|
|
202
|
+
local_time(
|
|
203
|
+
date,
|
|
204
|
+
source: source,
|
|
205
|
+
hour: source.hour,
|
|
206
|
+
minute: source.min,
|
|
207
|
+
second: source.sec,
|
|
208
|
+
sub_second: source.subsec
|
|
209
|
+
)
|
|
210
|
+
end
|
|
184
211
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
212
|
+
def local_time(date, source:, **clock)
|
|
213
|
+
clock = { hour: 0, minute: 0, second: 0, sub_second: 0 }.merge(clock)
|
|
214
|
+
config.tz.local_time(
|
|
215
|
+
date.year, date.month, date.day,
|
|
216
|
+
clock[:hour], clock[:minute], clock[:second], clock[:sub_second], source.dst?
|
|
217
|
+
)
|
|
218
|
+
rescue TZInfo::AmbiguousTime
|
|
219
|
+
resolve_ambiguous_time(date, clock, source)
|
|
220
|
+
rescue TZInfo::PeriodNotFound
|
|
221
|
+
resolve_missing_time(date, clock, source)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def resolve_ambiguous_time(date, clock, source)
|
|
225
|
+
timestamp = local_timestamp(date, clock)
|
|
226
|
+
periods = config.tz.periods_for_local(timestamp)
|
|
227
|
+
period = periods.find { |candidate| candidate.observed_utc_offset == source.utc_offset } || periods.first
|
|
228
|
+
config.tz.utc_to_local(Time.at(timestamp.to_r - period.observed_utc_offset).utc)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def resolve_missing_time(date, clock, source)
|
|
232
|
+
timestamp = local_timestamp(date, clock)
|
|
233
|
+
transition = missing_time_transition(timestamp)
|
|
234
|
+
raise TZInfo::PeriodNotFound, timestamp.to_s unless transition
|
|
235
|
+
|
|
236
|
+
gap = transition.offset.observed_utc_offset - transition.previous_offset.observed_utc_offset
|
|
237
|
+
shifted = timestamp + gap
|
|
238
|
+
shifted_clock = { hour: shifted.hour, minute: shifted.min, second: shifted.sec, sub_second: shifted.subsec }
|
|
239
|
+
local_time(shifted.to_date, source: source, **shifted_clock)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def missing_time_transition(timestamp)
|
|
243
|
+
config.tz.transitions_up_to(timestamp + 172_800, timestamp - 172_800).find do |transition|
|
|
244
|
+
missing_time_transition?(timestamp, transition)
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def missing_time_transition?(timestamp, transition)
|
|
249
|
+
previous_offset = transition.previous_offset.observed_utc_offset
|
|
250
|
+
next_offset = transition.offset.observed_utc_offset
|
|
251
|
+
return false unless next_offset > previous_offset
|
|
252
|
+
|
|
253
|
+
start_at = transition.at.to_time + previous_offset
|
|
254
|
+
end_at = transition.at.to_time + next_offset
|
|
255
|
+
timestamp >= start_at && timestamp < end_at
|
|
256
|
+
end
|
|
188
257
|
|
|
189
|
-
|
|
258
|
+
def local_timestamp(date, clock)
|
|
259
|
+
Time.utc(
|
|
260
|
+
date.year, date.month, date.day,
|
|
261
|
+
clock[:hour], clock[:minute], clock[:second] + clock[:sub_second]
|
|
262
|
+
)
|
|
190
263
|
end
|
|
191
264
|
|
|
192
265
|
def days_in_month(year, month)
|
|
@@ -26,16 +26,30 @@ module Trifle
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def perform
|
|
29
|
-
if
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
return direct_write if direct_write?
|
|
30
|
+
|
|
31
|
+
buffered_write
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def tracking_key
|
|
35
|
+
@untracked ? '__untracked__' : nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def direct_write?
|
|
41
|
+
config.driver.respond_to?(:direct_write)
|
|
42
|
+
end
|
|
38
43
|
|
|
44
|
+
def direct_write
|
|
45
|
+
config.driver.direct_write(**direct_payload)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def direct_payload
|
|
49
|
+
{ operation: :track, key: key, at: @at, values: values, untracked: @untracked }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def buffered_write
|
|
39
53
|
payload = {
|
|
40
54
|
keys: config.granularities.map { |granularity| key_for(granularity: granularity) },
|
|
41
55
|
values: values
|
|
@@ -48,12 +62,6 @@ module Trifle
|
|
|
48
62
|
end
|
|
49
63
|
end
|
|
50
64
|
|
|
51
|
-
def tracking_key
|
|
52
|
-
@untracked ? '__untracked__' : nil
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
private
|
|
56
|
-
|
|
57
65
|
def localized_time(time)
|
|
58
66
|
base_time = time.is_a?(Time) ? time : time.to_time
|
|
59
67
|
config.tz.utc_to_local(base_time.getutc)
|
|
@@ -26,16 +26,30 @@ module Trifle
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def perform
|
|
29
|
-
if
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
return direct_write if direct_write?
|
|
30
|
+
|
|
31
|
+
buffered_write
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def tracking_key
|
|
35
|
+
@untracked ? '__untracked__' : nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def direct_write?
|
|
41
|
+
config.driver.respond_to?(:direct_write)
|
|
42
|
+
end
|
|
38
43
|
|
|
44
|
+
def direct_write
|
|
45
|
+
config.driver.direct_write(**direct_payload)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def direct_payload
|
|
49
|
+
{ operation: :assert, key: key, at: @at, values: values, untracked: @untracked }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def buffered_write
|
|
39
53
|
payload = {
|
|
40
54
|
keys: config.granularities.map { |granularity| key_for(granularity: granularity) },
|
|
41
55
|
values: values
|
|
@@ -48,12 +62,6 @@ module Trifle
|
|
|
48
62
|
end
|
|
49
63
|
end
|
|
50
64
|
|
|
51
|
-
def tracking_key
|
|
52
|
-
@untracked ? '__untracked__' : nil
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
private
|
|
56
|
-
|
|
57
65
|
def localized_time(time)
|
|
58
66
|
base_time = time.is_a?(Time) ? time : time.to_time
|
|
59
67
|
config.tz.utc_to_local(base_time.getutc)
|
data/lib/trifle/stats/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trifle-stats
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jozef Vaclavik
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|