redis-objects-daily-counter 0.2.0 → 0.4.1

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 (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