redis-objects-periodical 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![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
|
+
|
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
|