timed-rediscounter 1.0.0 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/timed/rediscounter.rb +172 -2
- data/lib/timed/rediscounter/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a7b99e435001e68df58db161216416c209fb6dd
|
4
|
+
data.tar.gz: 5abc20bda6f7363fe184041e60c7a2d0015d2ac3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9dbd7645ed21eef5fbe2c7c6041fc7e74562e1094afac61e973f88150310ac560678373be955007a800f7f55624157d362fb0b2cfb23ae6b7352de58be23a328
|
7
|
+
data.tar.gz: a9b9f7228ea7270461a11d12ba95b17c40f23bdb5ce05a9c161bb1ba0347c5f72f1438b8600972869c8b0bfc66c76a02a2c9f3e7aaa80096af53d08e72a83787
|
data/lib/timed/rediscounter.rb
CHANGED
@@ -7,13 +7,15 @@ require 'active_support/core_ext/hash'
|
|
7
7
|
require "redis"
|
8
8
|
|
9
9
|
require "timed/rediscounter/version"
|
10
|
-
|
10
|
+
|
11
11
|
|
12
12
|
|
13
13
|
|
14
14
|
module Timed
|
15
15
|
module Rediscounter
|
16
|
-
|
16
|
+
def self.included(mod)
|
17
|
+
mod.extend(ClassMethods)
|
18
|
+
end
|
17
19
|
|
18
20
|
def self.redis=(r)
|
19
21
|
@redis = r if r.is_a?(Redis)
|
@@ -64,5 +66,173 @@ module Timed
|
|
64
66
|
end
|
65
67
|
|
66
68
|
|
69
|
+
class Counter
|
70
|
+
|
71
|
+
Periods = [:minute, :hour, :day, :month, :year].freeze
|
72
|
+
|
73
|
+
attr_reader :periods,:key
|
74
|
+
def initialize(key,default_options={})
|
75
|
+
@key = key
|
76
|
+
@periods = (default_options.delete(:periods) || Periods)
|
77
|
+
@redis = (default_options.delete(:redis) || Timed::Rediscounter.redis)
|
78
|
+
raise_if_not_valid_periods(@periods)
|
79
|
+
@default_options = default_options.to_h
|
80
|
+
end
|
81
|
+
|
82
|
+
# Increments all given period keys by a given offset
|
83
|
+
# offset is normally 1
|
84
|
+
#
|
85
|
+
def incr(options={})
|
86
|
+
opt = @default_options.merge(options).with_indifferent_access
|
87
|
+
offset = opt.fetch(:offset,1).to_i
|
88
|
+
time = opt.fetch(:time,Time.current)
|
89
|
+
periods = (opt[:periods] || @periods)
|
90
|
+
raise_if_not_valid_periods(periods)
|
91
|
+
|
92
|
+
if offset > 0
|
93
|
+
return redis.multi do
|
94
|
+
periods.each do |period|
|
95
|
+
redis.hincrby( period_key(period), convert_time_to_period_hash_key(time,period), offset)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
return []
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns a Hash by a given range or a period
|
104
|
+
#
|
105
|
+
# example:
|
106
|
+
# history(1.hour.ago..Time.now) or history(1.hour.ago)
|
107
|
+
#
|
108
|
+
#
|
109
|
+
# result:
|
110
|
+
# {2017-09-22 15:00:00 +0200=>0, 2017-09-22 16:00:00 +0200=>0}
|
111
|
+
#
|
112
|
+
# optional Parameter period:
|
113
|
+
# [:minute, :hour, :day, :month, :year]
|
114
|
+
def history(range_arg,period=nil)
|
115
|
+
redis_key, hash_keys = build_redishash_arguments(range_arg,period)
|
116
|
+
return Hash.new if hash_keys.empty?
|
117
|
+
|
118
|
+
redis.mapped_hmget(redis_key, *hash_keys).inject({}) do |h,(k,v)|
|
119
|
+
h[Time.at(k)] = v.to_i
|
120
|
+
h
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def sum(range_arg,period=nil)
|
125
|
+
redis_key, hash_keys = build_redishash_arguments(range_arg,period)
|
126
|
+
return 0 if hash_keys.empty?
|
127
|
+
redis.hmget(redis_key, *hash_keys).inject(0){|sum,i| sum += i.to_i}
|
128
|
+
end
|
129
|
+
|
130
|
+
#Expiring all period Keys
|
131
|
+
#
|
132
|
+
#expire_in in seconds
|
133
|
+
def expire_keys(expire_in=nil)
|
134
|
+
expire_in ||= @default_options.fetch(:expire_in, 1.year).to_i
|
135
|
+
redis.multi do
|
136
|
+
Periods.each { |period| redis.expire period_key(period), expire_in }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
#deleting all period keys
|
141
|
+
def delete_keys
|
142
|
+
redis.multi do
|
143
|
+
Periods.each { |period| redis.del period_key(period) }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
#helper to access redis
|
148
|
+
def redis
|
149
|
+
@redis
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
#builds the
|
155
|
+
def build_redishash_arguments(range_arg,period=nil)
|
156
|
+
case range_arg
|
157
|
+
when Time,Date
|
158
|
+
range = (range_arg..Time.current)
|
159
|
+
when String
|
160
|
+
range = (Time.parse(range_arg)..Time.current)
|
161
|
+
when Range
|
162
|
+
range = range_arg
|
163
|
+
else
|
164
|
+
ArgumentError.new
|
165
|
+
end
|
166
|
+
period ||= period_by_range(range)
|
167
|
+
raise_if_not_valid_periods(period)
|
168
|
+
|
169
|
+
off_set = 1.send(period)
|
170
|
+
hash_keys = Set.new
|
171
|
+
|
172
|
+
start_time = range.first.send("beginning_of_#{period}")
|
173
|
+
end_time = range.last.send("beginning_of_#{period}")
|
174
|
+
|
175
|
+
hash_keys << start_time
|
176
|
+
while (end_time - start_time) >= off_set
|
177
|
+
start_time += off_set
|
178
|
+
hash_keys << start_time
|
179
|
+
end
|
180
|
+
|
181
|
+
return period_key(period), hash_keys.collect(&:to_i)
|
182
|
+
end
|
183
|
+
|
184
|
+
Steps = [1.hour, 1.day, 1.month, 1.year, 2.year].freeze
|
185
|
+
# Calculate a a valid period by a given range
|
186
|
+
def period_by_range(range)
|
187
|
+
diff = (range.last - range.first).round
|
188
|
+
period = nil
|
189
|
+
Steps.each_with_index do |step,i|
|
190
|
+
if diff <= step
|
191
|
+
period = @periods[i]
|
192
|
+
break
|
193
|
+
end
|
194
|
+
end
|
195
|
+
#if not found => fallback
|
196
|
+
period ||= ( @periods[Steps.length] || @periods.last )
|
197
|
+
end
|
198
|
+
|
199
|
+
def raise_if_not_valid_periods(a)
|
200
|
+
r = case a
|
201
|
+
when Array
|
202
|
+
return !a.any?{|it| !Periods.include?(it.to_sym)}
|
203
|
+
when Symbol,String
|
204
|
+
return Periods.include?(a.to_sym)
|
205
|
+
else
|
206
|
+
false
|
207
|
+
end
|
208
|
+
raise ArgumentError.new("Not valid periods: #{a} Must contain one or more of #{Periods}") unless r
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
def period_key(period)
|
213
|
+
"#{self.class.name}::#{@key}::#{period}"
|
214
|
+
end
|
215
|
+
|
216
|
+
def convert_time_to_period_hash_key(time,period)
|
217
|
+
case time
|
218
|
+
when Time
|
219
|
+
t = time
|
220
|
+
when Date
|
221
|
+
t = time.to_time
|
222
|
+
when String
|
223
|
+
t = Time.parse(time)
|
224
|
+
when Fixnum,Float
|
225
|
+
t = Time.at(time)
|
226
|
+
else
|
227
|
+
raise ArgumentError.new("Not valid Time")
|
228
|
+
end
|
229
|
+
t.send("beginning_of_#{period}").to_i
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
|
67
237
|
end
|
68
238
|
end
|