redis-objects-daily-counter 0.2.0 → 0.4.1

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