redis-objects-periodical 0.4.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 +7 -0
- data/.circleci/config.yml +207 -0
- data/.gem_comet.yml +24 -0
- data/.github/dependabot.yml +19 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +40 -0
- data/.rubocop_todo.yml +16 -0
- data/CHANGELOG.md +112 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Dockerfile +7 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +98 -0
- data/LICENSE.txt +21 -0
- data/README.md +222 -0
- data/Rakefile +15 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docker-compose.yml +17 -0
- data/lib/redis/base_counter_object.rb +27 -0
- data/lib/redis/base_hash_key_object.rb +40 -0
- data/lib/redis/base_set_object.rb +28 -0
- data/lib/redis/objects/periodical/version.rb +9 -0
- 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 +36 -0
- data/redis-objects-periodical.gemspec +36 -0
- metadata +99 -0
data/README.md
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
[](https://circleci.com/gh/ryz310/redis-objects-periodical) [](https://badge.fury.io/rb/redis-objects-periodical) [](https://codeclimate.com/github/ryz310/redis-objects-periodical/maintainability) [](https://codeclimate.com/github/ryz310/redis-objects-periodical/test_coverage)
|
2
|
+
|
3
|
+
# Redis::Objects::Periodical
|
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 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
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'redis-objects-periodical'
|
13
|
+
```
|
14
|
+
|
15
|
+
If you want to know about installation and standard usage, please see Redis::Objects' GitHub page.
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
`daily_counter` and `daily_set` automatically creates keys that are unique to each object, in the format:
|
20
|
+
|
21
|
+
```
|
22
|
+
model_name:id:field_name:yyyy-mm-dd
|
23
|
+
```
|
24
|
+
|
25
|
+
I recommend using with `expireat` option.
|
26
|
+
For illustration purposes, consider this stub class:
|
27
|
+
|
28
|
+
```rb
|
29
|
+
class Homepage
|
30
|
+
include Redis::Objects
|
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
|
35
|
+
|
36
|
+
def id
|
37
|
+
1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# 2021-04-01
|
42
|
+
homepage = Homepage.new
|
43
|
+
homepage.id # 1
|
44
|
+
|
45
|
+
homepage.pv.increment
|
46
|
+
homepage.pv.increment
|
47
|
+
homepage.pv.increment
|
48
|
+
puts homepage.pv.value # 3
|
49
|
+
|
50
|
+
# 2021-04-02 (next day)
|
51
|
+
puts homepage.pv.value # 0
|
52
|
+
homepage.pv.increment
|
53
|
+
homepage.pv.increment
|
54
|
+
puts homepage.pv.value # 2
|
55
|
+
|
56
|
+
start_date = Date.new(2021, 4, 1)
|
57
|
+
end_date = Date.new(2021, 4, 2)
|
58
|
+
homepage.pv.range(start_date, end_date) # [3, 2]
|
59
|
+
```
|
60
|
+
|
61
|
+
### Periodical Counters
|
62
|
+
|
63
|
+
The periodical counters automatically switches the save destination when the date changes.
|
64
|
+
You can access past dates counted values like Ruby arrays:
|
65
|
+
|
66
|
+
```rb
|
67
|
+
# 2021-04-01
|
68
|
+
homepage.pv.increment(3)
|
69
|
+
|
70
|
+
# 2021-04-02 (next day)
|
71
|
+
homepage.pv.increment(2)
|
72
|
+
|
73
|
+
# 2021-04-03 (next day)
|
74
|
+
homepage.pv.increment(5)
|
75
|
+
|
76
|
+
homepage.pv[Date.new(2021, 4, 1)] # => 3
|
77
|
+
homepage.pv[Date.new(2021, 4, 1), 3] # => [3, 2, 5]
|
78
|
+
homepage.pv[Date.new(2021, 4, 1)..Date.new(2021, 4, 2)] # => [3, 2]
|
79
|
+
|
80
|
+
homepage.pv.delete_at(Date.new(2021, 4, 1))
|
81
|
+
homepage.pv.range(Date.new(2021, 4, 1), Date.new(2021, 4, 3)) # => [0, 2, 5]
|
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
|
84
|
+
```
|
85
|
+
|
86
|
+
#### Periodical Counters Family
|
87
|
+
|
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
|
+
```
|
181
|
+
|
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`
|
197
|
+
|
198
|
+
### Timezone
|
199
|
+
|
200
|
+
This gem follows Ruby process' time zone, but if you extends Time class by ActiveSupport (e.g. `Time.current`), follows Rails process' timezone.
|
201
|
+
|
202
|
+
## Development
|
203
|
+
|
204
|
+
The development environment for this gem is configured with docker-compose.
|
205
|
+
Please use the following command:
|
206
|
+
|
207
|
+
$ docker-compose up -d
|
208
|
+
$ docker-compose run --rm ruby bundle
|
209
|
+
$ docker-compose run --rm ruby rspec .
|
210
|
+
$ docker-compose run --rm ruby rubocop -a
|
211
|
+
|
212
|
+
## Contributing
|
213
|
+
|
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).
|
215
|
+
|
216
|
+
## License
|
217
|
+
|
218
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
219
|
+
|
220
|
+
## Code of Conduct
|
221
|
+
|
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
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_helper'
|
4
|
+
|
5
|
+
Bundler::GemHelper.install_tasks(name: 'redis-objects-periodical')
|
6
|
+
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
|
+
|
11
|
+
require 'rubocop/rake_task'
|
12
|
+
|
13
|
+
RuboCop::RakeTask.new
|
14
|
+
|
15
|
+
task default: %i[spec rubocop]
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'redis-objects-periodical'
|
6
|
+
|
7
|
+
Redis::Objects.redis = Redis.new(host: 'redis', port: 6379)
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/docker-compose.yml
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
module BaseCounterObject
|
5
|
+
private
|
6
|
+
|
7
|
+
def get_redis_object(key)
|
8
|
+
Redis::Counter.new(key)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_value_from_redis(key)
|
12
|
+
redis.get(key).to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_values_from_redis(keys)
|
16
|
+
redis.mget(*keys).map(&:to_i)
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete_from_redis(key)
|
20
|
+
redis.del(key)
|
21
|
+
end
|
22
|
+
|
23
|
+
def empty_value
|
24
|
+
[]
|
25
|
+
end
|
26
|
+
end
|
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
|