redis-objects-periodical 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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