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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +207 -0
  3. data/.gem_comet.yml +24 -0
  4. data/.github/dependabot.yml +19 -0
  5. data/.gitignore +11 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +40 -0
  8. data/.rubocop_todo.yml +16 -0
  9. data/CHANGELOG.md +112 -0
  10. data/CODE_OF_CONDUCT.md +84 -0
  11. data/Dockerfile +7 -0
  12. data/Gemfile +18 -0
  13. data/Gemfile.lock +98 -0
  14. data/LICENSE.txt +21 -0
  15. data/README.md +222 -0
  16. data/Rakefile +15 -0
  17. data/bin/console +14 -0
  18. data/bin/setup +8 -0
  19. data/docker-compose.yml +17 -0
  20. data/lib/redis/base_counter_object.rb +27 -0
  21. data/lib/redis/base_hash_key_object.rb +40 -0
  22. data/lib/redis/base_set_object.rb +28 -0
  23. data/lib/redis/objects/periodical/version.rb +9 -0
  24. data/lib/redis/objects/periodical_counters.rb +40 -0
  25. data/lib/redis/objects/periodical_hashes.rb +50 -0
  26. data/lib/redis/objects/periodical_sets.rb +50 -0
  27. data/lib/redis/periodical_counter.rb +15 -0
  28. data/lib/redis/periodical_hash_key.rb +15 -0
  29. data/lib/redis/periodical_set.rb +15 -0
  30. data/lib/redis/recurring_at_intervals/annual.rb +18 -0
  31. data/lib/redis/recurring_at_intervals/daily.rb +18 -0
  32. data/lib/redis/recurring_at_intervals/hourly.rb +18 -0
  33. data/lib/redis/recurring_at_intervals/minutely.rb +18 -0
  34. data/lib/redis/recurring_at_intervals/monthly.rb +18 -0
  35. data/lib/redis/recurring_at_intervals/weekly.rb +18 -0
  36. data/lib/redis/recurring_at_intervals.rb +79 -0
  37. data/lib/redis-objects-periodical.rb +36 -0
  38. data/redis-objects-daily-counter.gemspec +36 -0
  39. data/redis-objects-periodical.gemspec +36 -0
  40. 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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,17 @@
1
+ version: '3'
2
+
3
+ services:
4
+ ruby:
5
+ build: .
6
+ depends_on:
7
+ - redis
8
+ volumes:
9
+ - .:/my_app
10
+ - bundle:/usr/local/bundle
11
+ redis:
12
+ image: redis:latest
13
+ ports:
14
+ - 6379:6379
15
+
16
+ volumes:
17
+ bundle:
@@ -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,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Objects
5
+ module Periodical
6
+ VERSION = '0.4.0'
7
+ end
8
+ end
9
+ 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