timed-rediscounter 1.0.0 → 1.0.6
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/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
|