redis-objects-daily-counter 0.2.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +23 -20
- data/.gem_comet.yml +1 -1
- data/.github/dependabot.yml +19 -0
- data/.rubocop.yml +5 -4
- data/.rubocop_todo.yml +3 -19
- data/CHANGELOG.md +106 -9
- data/Dockerfile +1 -1
- data/Gemfile +3 -3
- data/Gemfile.lock +32 -29
- data/README.md +125 -25
- data/Rakefile +4 -1
- data/bin/console +2 -3
- data/lib/redis/base_counter_object.rb +11 -38
- data/lib/redis/base_hash_key_object.rb +40 -0
- data/lib/redis/base_set_object.rb +28 -0
- data/lib/redis/objects/{daily-counter → periodical}/version.rb +2 -4
- data/lib/redis/objects/periodical_counters.rb +40 -0
- data/lib/redis/objects/periodical_hashes.rb +50 -0
- data/lib/redis/objects/periodical_sets.rb +50 -0
- data/lib/redis/periodical_counter.rb +15 -0
- data/lib/redis/periodical_hash_key.rb +15 -0
- data/lib/redis/periodical_set.rb +15 -0
- data/lib/redis/recurring_at_intervals/annual.rb +18 -0
- data/lib/redis/recurring_at_intervals/daily.rb +18 -0
- data/lib/redis/recurring_at_intervals/hourly.rb +18 -0
- data/lib/redis/recurring_at_intervals/minutely.rb +18 -0
- data/lib/redis/recurring_at_intervals/monthly.rb +18 -0
- data/lib/redis/recurring_at_intervals/weekly.rb +18 -0
- data/lib/redis/recurring_at_intervals.rb +79 -0
- data/lib/redis-objects-periodical.rb +36 -0
- data/redis-objects-daily-counter.gemspec +5 -4
- data/redis-objects-periodical.gemspec +36 -0
- metadata +26 -19
- data/lib/redis/annual_counter.rb +0 -18
- data/lib/redis/daily_counter.rb +0 -18
- data/lib/redis/hourly_counter.rb +0 -26
- data/lib/redis/minutely_counter.rb +0 -26
- data/lib/redis/monthly_counter.rb +0 -18
- data/lib/redis/objects/annual_counters.rb +0 -41
- data/lib/redis/objects/daily_counters.rb +0 -41
- data/lib/redis/objects/hourly_counters.rb +0 -41
- data/lib/redis/objects/minutely_counters.rb +0 -41
- data/lib/redis/objects/monthly_counters.rb +0 -41
- data/lib/redis/objects/weekly_counters.rb +0 -41
- data/lib/redis/weekly_counter.rb +0 -18
- data/lib/redis-objects-daily-counter.rb +0 -37
data/README.md
CHANGED
@@ -1,27 +1,28 @@
|
|
1
|
-
[![CircleCI](https://circleci.com/gh/ryz310/redis-objects-
|
1
|
+
[![CircleCI](https://circleci.com/gh/ryz310/redis-objects-periodical.svg?style=svg)](https://circleci.com/gh/ryz310/redis-objects-periodical) [![Gem Version](https://badge.fury.io/rb/redis-objects-periodical.svg)](https://badge.fury.io/rb/redis-objects-periodical) [![Maintainability](https://api.codeclimate.com/v1/badges/deebb6c406c306a6f337/maintainability)](https://codeclimate.com/github/ryz310/redis-objects-periodical/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/deebb6c406c306a6f337/test_coverage)](https://codeclimate.com/github/ryz310/redis-objects-periodical/test_coverage)
|
2
2
|
|
3
|
-
# Redis::Objects::
|
3
|
+
# Redis::Objects::Periodical
|
4
4
|
|
5
|
-
This is a gem which extends [Redis::Objects](https://github.com/nateware/redis-objects) gem. Once install this gem, you can use the
|
5
|
+
This is a gem which extends [Redis::Objects](https://github.com/nateware/redis-objects) gem. Once install this gem, you can use the periodical counter, the periodical set, etc. in addition to the standard features of Redis::Objects. These counters and sets are useful for measuring conversions, implementing API rate limiting, MAU, DAU, and more.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
9
|
Add this line to your application's Gemfile:
|
10
10
|
|
11
11
|
```ruby
|
12
|
-
gem 'redis-objects-
|
12
|
+
gem 'redis-objects-periodical'
|
13
13
|
```
|
14
14
|
|
15
15
|
If you want to know about installation and standard usage, please see Redis::Objects' GitHub page.
|
16
16
|
|
17
17
|
## Usage
|
18
18
|
|
19
|
-
`daily_counter` automatically creates keys that are unique to each object, in the format:
|
19
|
+
`daily_counter` and `daily_set` automatically creates keys that are unique to each object, in the format:
|
20
20
|
|
21
21
|
```
|
22
22
|
model_name:id:field_name:yyyy-mm-dd
|
23
23
|
```
|
24
24
|
|
25
|
+
I recommend using with `expireat` option.
|
25
26
|
For illustration purposes, consider this stub class:
|
26
27
|
|
27
28
|
```rb
|
@@ -29,6 +30,8 @@ class Homepage
|
|
29
30
|
include Redis::Objects
|
30
31
|
|
31
32
|
daily_counter :pv, expireat: -> { Time.now + 2_678_400 } # about a month
|
33
|
+
daily_hash_key :browsing_history, expireat: -> { Time.now + 2_678_400 } # about a month
|
34
|
+
daily_set :dau, expireat: -> { Time.now + 2_678_400 } # about a month
|
32
35
|
|
33
36
|
def id
|
34
37
|
1
|
@@ -55,7 +58,9 @@ end_date = Date.new(2021, 4, 2)
|
|
55
58
|
homepage.pv.range(start_date, end_date) # [3, 2]
|
56
59
|
```
|
57
60
|
|
58
|
-
|
61
|
+
### Periodical Counters
|
62
|
+
|
63
|
+
The periodical counters automatically switches the save destination when the date changes.
|
59
64
|
You can access past dates counted values like Ruby arrays:
|
60
65
|
|
61
66
|
```rb
|
@@ -72,28 +77,123 @@ homepage.pv[Date.new(2021, 4, 1)] # => 3
|
|
72
77
|
homepage.pv[Date.new(2021, 4, 1), 3] # => [3, 2, 5]
|
73
78
|
homepage.pv[Date.new(2021, 4, 1)..Date.new(2021, 4, 2)] # => [3, 2]
|
74
79
|
|
75
|
-
homepage.pv.
|
80
|
+
homepage.pv.delete_at(Date.new(2021, 4, 1))
|
76
81
|
homepage.pv.range(Date.new(2021, 4, 1), Date.new(2021, 4, 3)) # => [0, 2, 5]
|
77
|
-
homepage.pv.at(Date.new(2021, 4, 2)) # =>
|
82
|
+
homepage.pv.at(Date.new(2021, 4, 2)) # => #<Redis::Counter key="homepage:1:pv:2021-04-02">
|
83
|
+
homepage.pv.at(Date.new(2021, 4, 2)).value # 2
|
78
84
|
```
|
79
85
|
|
80
|
-
|
86
|
+
#### Periodical Counters Family
|
81
87
|
|
82
|
-
|
88
|
+
- `annual_counter`
|
89
|
+
- Key format: `model_name:id:field_name:yyyy`
|
90
|
+
- Redis is a highly volatile key-value store, so I don't recommend using it.
|
91
|
+
- `monthly_counter`
|
92
|
+
- Key format: `model_name:id:field_name:yyyy-mm`
|
93
|
+
- `weekly_counter`
|
94
|
+
- Key format: `model_name:id:field_name:yyyyWw`
|
95
|
+
- `daily_counter`
|
96
|
+
- Key format: `model_name:id:field_name:yyyy-mm-dd`
|
97
|
+
- `hourly_counter`
|
98
|
+
- Key format: `model_name:id:field_name:yyyy-mm-ddThh`
|
99
|
+
- `minutely_counter`
|
100
|
+
- Key format: `model_name:id:field_name:yyyy-mm-ddThh:mi`
|
101
|
+
|
102
|
+
### Periodical Hashes
|
103
|
+
|
104
|
+
The periodical hashes also automatically switches the save destination when the date changes.
|
105
|
+
|
106
|
+
```rb
|
107
|
+
# 2021-04-01
|
108
|
+
homepage.browsing_history.incr('item1')
|
109
|
+
homepage.browsing_history.incr('item2')
|
110
|
+
homepage.browsing_history.incr('item2')
|
111
|
+
puts homepage.browsing_history.all # { 'item1' => '1', 'item2' => '2' }
|
112
|
+
|
113
|
+
# 2021-04-02 (next day)
|
114
|
+
puts homepage.browsing_history.all # {}
|
115
|
+
|
116
|
+
homepage.browsing_history.bulk_set('item1' => 3, 'item3' => 5)
|
117
|
+
puts homepage.browsing_history.all # { 'item1' => '3', 'item3' => '5' }
|
118
|
+
|
119
|
+
# 2021-04-03 (next day)
|
120
|
+
homepage.browsing_history.incr('item2')
|
121
|
+
homepage.browsing_history.incr('item4')
|
122
|
+
puts homepage.browsing_history.all # { 'item2' => '1', 'item4' => '1' }
|
123
|
+
|
124
|
+
homepage.browsing_history[Date.new(2021, 4, 1)] # => { 'item1' => '1', 'item2' => '2' }
|
125
|
+
homepage.browsing_history[Date.new(2021, 4, 1), 3] # => { 'item1' => '4', 'item2' => '3', 'item3' => '5', 'item4' => '1' }
|
126
|
+
homepage.browsing_history[Date.new(2021, 4, 1)..Date.new(2021, 4, 2)] # => { 'item1' => '4', 'item2' => '2', 'item3' => '5' }
|
127
|
+
|
128
|
+
homepage.browsing_history.delete_at(Date.new(2021, 4, 1))
|
129
|
+
homepage.browsing_history.range(Date.new(2021, 4, 1), Date.new(2021, 4, 3)) # => { 'item1' => '3', 'item2' => '1', 'item3' => '5', 'item4' => '1' }
|
130
|
+
homepage.browsing_history.at(Date.new(2021, 4, 2)) # => #<Redis::HashKey key="homepage:1:browsing_history:2021-04-02">
|
131
|
+
homepage.browsing_history.at(Date.new(2021, 4, 2)).all # { 'item1' => '3', 'item3' => '5' }
|
132
|
+
```
|
133
|
+
|
134
|
+
#### Periodical Hashes Family
|
135
|
+
|
136
|
+
- `annual_hash_key`
|
137
|
+
- Key format: `model_name:id:field_name:yyyy`
|
138
|
+
- Redis is a highly volatile key-value store, so I don't recommend using it.
|
139
|
+
- `monthly_hash_key`
|
140
|
+
- Key format: `model_name:id:field_name:yyyy-mm`
|
141
|
+
- `weekly_hash_key`
|
142
|
+
- Key format: `model_name:id:field_name:yyyyWw`
|
143
|
+
- `daily_hash_key`
|
144
|
+
- Key format: `model_name:id:field_name:yyyy-mm-dd`
|
145
|
+
- `hourly_hash_key`
|
146
|
+
- Key format: `model_name:id:field_name:yyyy-mm-ddThh`
|
147
|
+
- `minutely_hash_key`
|
148
|
+
- Key format: `model_name:id:field_name:yyyy-mm-ddThh:mi`
|
149
|
+
|
150
|
+
### Periodical Sets
|
151
|
+
|
152
|
+
The periodical sets also automatically switches the save destination when the date changes.
|
153
|
+
|
154
|
+
```rb
|
155
|
+
# 2021-04-01
|
156
|
+
homepage.dau << 'user1'
|
157
|
+
homepage.dau << 'user2'
|
158
|
+
homepage.dau << 'user1' # dup ignored
|
159
|
+
puts homepage.dau.members # ['user1', 'user2']
|
160
|
+
puts homepage.dau.length # 2
|
161
|
+
puts homepage.dau.count # alias of #length
|
162
|
+
|
163
|
+
# 2021-04-02 (next day)
|
164
|
+
puts homepage.dau.members # []
|
165
|
+
|
166
|
+
homepage.dau.merge('user2', 'user3')
|
167
|
+
puts homepage.dau.members # ['user2', 'user3']
|
168
|
+
|
169
|
+
# 2021-04-03 (next day)
|
170
|
+
homepage.dau.merge('user4')
|
171
|
+
|
172
|
+
homepage.dau[Date.new(2021, 4, 1)] # => ['user1', 'user2']
|
173
|
+
homepage.dau[Date.new(2021, 4, 1), 3] # => ['user1', 'user2', 'user3', 'user4']
|
174
|
+
homepage.dau[Date.new(2021, 4, 1)..Date.new(2021, 4, 2)] # => ['user1', 'user2', 'user3']
|
175
|
+
|
176
|
+
homepage.dau.delete_at(Date.new(2021, 4, 1))
|
177
|
+
homepage.dau.range(Date.new(2021, 4, 1), Date.new(2021, 4, 3)) # => ['user2', 'user3', 'user4']
|
178
|
+
homepage.dau.at(Date.new(2021, 4, 2)) # => #<Redis::Set key="homepage:1:dau:2021-04-02">
|
179
|
+
homepage.dau.at(Date.new(2021, 4, 2)).members # ['user2', 'user3']
|
180
|
+
```
|
83
181
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
182
|
+
#### Periodical Sets Family
|
183
|
+
|
184
|
+
- `annual_set`
|
185
|
+
- Key format: `model_name:id:field_name:yyyy`
|
186
|
+
- Redis is a highly volatile key-value store, so I don't recommend using it.
|
187
|
+
- `monthly_set`
|
188
|
+
- Key format: `model_name:id:field_name:yyyy-mm`
|
189
|
+
- `weekly_set`
|
190
|
+
- Key format: `model_name:id:field_name:yyyyWw`
|
191
|
+
- `daily_set`
|
192
|
+
- Key format: `model_name:id:field_name:yyyy-mm-dd`
|
193
|
+
- `hourly_set`
|
194
|
+
- Key format: `model_name:id:field_name:yyyy-mm-ddThh`
|
195
|
+
- `minutely_set`
|
196
|
+
- Key format: `model_name:id:field_name:yyyy-mm-ddThh:mi`
|
97
197
|
|
98
198
|
### Timezone
|
99
199
|
|
@@ -111,7 +211,7 @@ Please use the following command:
|
|
111
211
|
|
112
212
|
## Contributing
|
113
213
|
|
114
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/redis-objects-
|
214
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/redis-objects-periodical. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/redis-objects-periodical/blob/master/CODE_OF_CONDUCT.md).
|
115
215
|
|
116
216
|
## License
|
117
217
|
|
@@ -119,4 +219,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
119
219
|
|
120
220
|
## Code of Conduct
|
121
221
|
|
122
|
-
Everyone interacting in the Redis::Objects::Daily::Counter project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/redis-objects-
|
222
|
+
Everyone interacting in the Redis::Objects::Daily::Counter project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/redis-objects-periodical/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -2,10 +2,9 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'bundler/setup'
|
5
|
-
require 'redis-objects-
|
5
|
+
require 'redis-objects-periodical'
|
6
6
|
|
7
|
-
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
7
|
+
Redis::Objects.redis = Redis.new(host: 'redis', port: 6379)
|
9
8
|
|
10
9
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
10
|
# require "pry"
|
@@ -1,54 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Redis
|
4
|
-
|
5
|
-
|
6
|
-
@original_key = key
|
7
|
-
super(redis_daily_field_key(current_time), *args)
|
8
|
-
end
|
9
|
-
|
10
|
-
attr_reader :original_key
|
4
|
+
module BaseCounterObject
|
5
|
+
private
|
11
6
|
|
12
|
-
def
|
13
|
-
|
14
|
-
range(date_or_time.first, date_or_time.max)
|
15
|
-
elsif length
|
16
|
-
case length <=> 0
|
17
|
-
when 1 then range(date_or_time, next_key(date_or_time, length))
|
18
|
-
when 0 then []
|
19
|
-
when -1 then nil # Ruby does this (a bit weird)
|
20
|
-
end
|
21
|
-
else
|
22
|
-
at(date_or_time)
|
23
|
-
end
|
7
|
+
def get_redis_object(key)
|
8
|
+
Redis::Counter.new(key)
|
24
9
|
end
|
25
|
-
alias slice []
|
26
10
|
|
27
|
-
def
|
28
|
-
redis.
|
11
|
+
def get_value_from_redis(key)
|
12
|
+
redis.get(key).to_i
|
29
13
|
end
|
30
14
|
|
31
|
-
def
|
32
|
-
keys = (start_date..end_date).map { |date| redis_daily_field_key(date) }.uniq
|
15
|
+
def get_values_from_redis(keys)
|
33
16
|
redis.mget(*keys).map(&:to_i)
|
34
17
|
end
|
35
18
|
|
36
|
-
def
|
37
|
-
redis.
|
38
|
-
end
|
39
|
-
|
40
|
-
def current_time
|
41
|
-
@current_time ||= Time.respond_to?(:current) ? Time.current : Time.now
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def redis_daily_field_key(_date_or_time)
|
47
|
-
raise 'not implemented'
|
19
|
+
def delete_from_redis(key)
|
20
|
+
redis.del(key)
|
48
21
|
end
|
49
22
|
|
50
|
-
def
|
51
|
-
|
23
|
+
def empty_value
|
24
|
+
[]
|
52
25
|
end
|
53
26
|
end
|
54
27
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module BaseHashKeyObject
|
5
|
+
private
|
6
|
+
|
7
|
+
def get_redis_object(key)
|
8
|
+
Redis::HashKey.new(key)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_value_from_redis(key)
|
12
|
+
h = redis.hgetall(key) || {}
|
13
|
+
h.each { |k, v| h[k] = unmarshal(v, options[:marshal_keys][k]) }
|
14
|
+
h
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_values_from_redis(keys) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
18
|
+
keys.inject({}) do |memo, key|
|
19
|
+
memo.merge(get_value_from_redis(key)) do |_, self_val, other_val|
|
20
|
+
values = [self_val, other_val]
|
21
|
+
if values.all? { |val| val =~ /\A\d+\z/ }
|
22
|
+
values.sum(&:to_i).to_s
|
23
|
+
elsif values.all? { |val| val =~ /\A\d+(\.\d+)?\z/ }
|
24
|
+
values.sum(&:to_f).to_s
|
25
|
+
else
|
26
|
+
values.join(',')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete_from_redis(key)
|
33
|
+
redis.del(key)
|
34
|
+
end
|
35
|
+
|
36
|
+
def empty_value
|
37
|
+
{}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module BaseSetObject
|
5
|
+
private
|
6
|
+
|
7
|
+
def get_redis_object(key)
|
8
|
+
Redis::Set.new(key)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_value_from_redis(key)
|
12
|
+
vals = redis.smembers(key)
|
13
|
+
vals.nil? ? [] : vals.map { |v| unmarshal(v) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_values_from_redis(keys)
|
17
|
+
redis.sunion(*keys).map { |v| unmarshal(v) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete_from_redis(key)
|
21
|
+
redis.del(key)
|
22
|
+
end
|
23
|
+
|
24
|
+
def empty_value
|
25
|
+
[]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis/periodical_counter'
|
4
|
+
|
5
|
+
Redis::PERIODICALS.each do |periodical| # rubocop:disable Metrics/BlockLength
|
6
|
+
new_module = Module.new
|
7
|
+
new_module.module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
8
|
+
def self.included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def #{periodical}_counter(name, options = {})
|
14
|
+
options[:start] ||= 0
|
15
|
+
options[:type] ||= (options[:start]).zero? ? :increment : :decrement
|
16
|
+
redis_objects[name.to_sym] = options.merge(type: :counter)
|
17
|
+
|
18
|
+
mod = Module.new do
|
19
|
+
define_method(name) do
|
20
|
+
Redis::#{periodical.capitalize}Counter.new(
|
21
|
+
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if options[:global]
|
27
|
+
extend mod
|
28
|
+
|
29
|
+
# dispatch to class methods
|
30
|
+
define_method(name) do
|
31
|
+
self.class.public_send(name)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
include mod
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
RUBY
|
39
|
+
Redis::Objects.const_set("#{periodical.capitalize}Counters", new_module)
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis/periodical_hash_key'
|
4
|
+
|
5
|
+
Redis::PERIODICALS.each do |periodical| # rubocop:disable Metrics/BlockLength
|
6
|
+
new_module = Module.new
|
7
|
+
new_module.module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
8
|
+
def self.included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
# Class methods that appear in your class when you include Redis::Objects.
|
13
|
+
module ClassMethods
|
14
|
+
# Define a new hash key. It will function like a regular instance
|
15
|
+
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
16
|
+
def #{periodical}_hash_key(name, options = {})
|
17
|
+
redis_objects[name.to_sym] = options.merge(type: :dict)
|
18
|
+
|
19
|
+
mod = Module.new do
|
20
|
+
define_method(name) do
|
21
|
+
Redis::#{periodical.capitalize}HashKey.new(
|
22
|
+
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
define_method(:"#\{name\}=") do |values|
|
27
|
+
hash_key = public_send(name)
|
28
|
+
|
29
|
+
redis.pipelined do
|
30
|
+
hash_key.clear
|
31
|
+
hash_key.bulk_set(values)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if options[:global]
|
37
|
+
extend mod
|
38
|
+
|
39
|
+
# dispatch to class methods
|
40
|
+
define_method(name) do
|
41
|
+
self.class.public_send(name)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
include mod
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
RUBY
|
49
|
+
Redis::Objects.const_set("#{periodical.capitalize}Hashes", new_module)
|
50
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis/periodical_set'
|
4
|
+
|
5
|
+
Redis::PERIODICALS.each do |periodical| # rubocop:disable Metrics/BlockLength
|
6
|
+
new_module = Module.new
|
7
|
+
new_module.module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
8
|
+
def self.included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
# Class methods that appear in your class when you include Redis::Objects.
|
13
|
+
module ClassMethods
|
14
|
+
# Define a new list. It will function like a regular instance
|
15
|
+
# method, so it can be used alongside ActiveRecord, DataMapper, etc.
|
16
|
+
def #{periodical}_set(name, options = {})
|
17
|
+
redis_objects[name.to_sym] = options.merge(type: :set)
|
18
|
+
|
19
|
+
mod = Module.new do
|
20
|
+
define_method(name) do
|
21
|
+
Redis::#{periodical.capitalize}Set.new(
|
22
|
+
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
define_method(:"#\{name\}=") do |values|
|
27
|
+
set = public_send(name)
|
28
|
+
|
29
|
+
redis.pipelined do
|
30
|
+
set.clear
|
31
|
+
set.merge(*values)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if options[:global]
|
37
|
+
extend mod
|
38
|
+
|
39
|
+
# dispatch to class methods
|
40
|
+
define_method(name) do
|
41
|
+
self.class.public_send(name)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
include mod
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
RUBY
|
49
|
+
Redis::Objects.const_set("#{periodical.capitalize}Sets", new_module)
|
50
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/recurring_at_intervals"
|
4
|
+
require "#{File.dirname(__FILE__)}/base_counter_object"
|
5
|
+
|
6
|
+
Redis::PERIODICALS.each do |periodical|
|
7
|
+
require "#{File.dirname(__FILE__)}/recurring_at_intervals/#{periodical}"
|
8
|
+
|
9
|
+
new_class = Class.new(Redis::Counter) do
|
10
|
+
include Redis::RecurringAtIntervals
|
11
|
+
include Redis::BaseCounterObject
|
12
|
+
include const_get("Redis::RecurringAtIntervals::#{periodical.capitalize}")
|
13
|
+
end
|
14
|
+
Redis.const_set("#{periodical.capitalize}Counter", new_class)
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/recurring_at_intervals"
|
4
|
+
require "#{File.dirname(__FILE__)}/base_hash_key_object"
|
5
|
+
|
6
|
+
Redis::PERIODICALS.each do |periodical|
|
7
|
+
require "#{File.dirname(__FILE__)}/recurring_at_intervals/#{periodical}"
|
8
|
+
|
9
|
+
new_class = Class.new(Redis::HashKey) do
|
10
|
+
include Redis::RecurringAtIntervals
|
11
|
+
include Redis::BaseHashKeyObject
|
12
|
+
include const_get("Redis::RecurringAtIntervals::#{periodical.capitalize}")
|
13
|
+
end
|
14
|
+
Redis.const_set("#{periodical.capitalize}HashKey", new_class)
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/recurring_at_intervals"
|
4
|
+
require "#{File.dirname(__FILE__)}/base_set_object"
|
5
|
+
|
6
|
+
Redis::PERIODICALS.each do |periodical|
|
7
|
+
require "#{File.dirname(__FILE__)}/recurring_at_intervals/#{periodical}"
|
8
|
+
|
9
|
+
new_class = Class.new(Redis::Set) do
|
10
|
+
include Redis::RecurringAtIntervals
|
11
|
+
include Redis::BaseSetObject
|
12
|
+
include const_get("Redis::RecurringAtIntervals::#{periodical.capitalize}")
|
13
|
+
end
|
14
|
+
Redis.const_set("#{periodical.capitalize}Set", new_class)
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module RecurringAtIntervals
|
5
|
+
module Annual
|
6
|
+
private
|
7
|
+
|
8
|
+
def redis_daily_field_key(date_or_time)
|
9
|
+
date_key = date_or_time.strftime('%Y')
|
10
|
+
[original_key, date_key].flatten.join(':')
|
11
|
+
end
|
12
|
+
|
13
|
+
def next_key(date, length = 1)
|
14
|
+
date.next_year(length)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module RecurringAtIntervals
|
5
|
+
module Daily
|
6
|
+
private
|
7
|
+
|
8
|
+
def redis_daily_field_key(date_or_time)
|
9
|
+
date_key = date_or_time.strftime('%Y-%m-%d')
|
10
|
+
[original_key, date_key].flatten.join(':')
|
11
|
+
end
|
12
|
+
|
13
|
+
def next_key(date, length = 1)
|
14
|
+
date + length
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module RecurringAtIntervals
|
5
|
+
module Hourly
|
6
|
+
private
|
7
|
+
|
8
|
+
def redis_daily_field_key(time)
|
9
|
+
time_key = time.strftime('%Y-%m-%dT%H')
|
10
|
+
[original_key, time_key].flatten.join(':')
|
11
|
+
end
|
12
|
+
|
13
|
+
def next_key(time, length = 1)
|
14
|
+
time + 3600 * length
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module RecurringAtIntervals
|
5
|
+
module Minutely
|
6
|
+
private
|
7
|
+
|
8
|
+
def redis_daily_field_key(time)
|
9
|
+
time_key = time.strftime('%Y-%m-%dT%H:%M')
|
10
|
+
[original_key, time_key].flatten.join(':')
|
11
|
+
end
|
12
|
+
|
13
|
+
def next_key(time, length = 1)
|
14
|
+
time + 60 * length
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module RecurringAtIntervals
|
5
|
+
module Monthly
|
6
|
+
private
|
7
|
+
|
8
|
+
def redis_daily_field_key(date_or_time)
|
9
|
+
date_key = date_or_time.strftime('%Y-%m')
|
10
|
+
[original_key, date_key].flatten.join(':')
|
11
|
+
end
|
12
|
+
|
13
|
+
def next_key(date, length = 1)
|
14
|
+
date.next_month(length)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|