redis-objects-daily-counter 0.2.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.
- checksums.yaml +7 -0
- data/.circleci/config.yml +204 -0
- data/.gem_comet.yml +24 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +39 -0
- data/.rubocop_todo.yml +32 -0
- data/CHANGELOG.md +21 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Dockerfile +7 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +95 -0
- data/LICENSE.txt +21 -0
- data/README.md +122 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/docker-compose.yml +17 -0
- data/lib/redis/annual_counter.rb +18 -0
- data/lib/redis/base_counter_object.rb +54 -0
- data/lib/redis/daily_counter.rb +18 -0
- data/lib/redis/hourly_counter.rb +26 -0
- data/lib/redis/minutely_counter.rb +26 -0
- data/lib/redis/monthly_counter.rb +18 -0
- data/lib/redis/objects/annual_counters.rb +41 -0
- data/lib/redis/objects/daily-counter/version.rb +11 -0
- data/lib/redis/objects/daily_counters.rb +41 -0
- data/lib/redis/objects/hourly_counters.rb +41 -0
- data/lib/redis/objects/minutely_counters.rb +41 -0
- data/lib/redis/objects/monthly_counters.rb +41 -0
- data/lib/redis/objects/weekly_counters.rb +41 -0
- data/lib/redis/weekly_counter.rb +18 -0
- data/lib/redis-objects-daily-counter.rb +37 -0
- data/redis-objects-daily-counter.gemspec +35 -0
- metadata +93 -0
data/README.md
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
[](https://circleci.com/gh/ryz310/redis-objects-daily-counter) [](https://badge.fury.io/rb/redis-objects-daily-counter) [](https://codeclimate.com/github/ryz310/redis-objects-daily-counter/maintainability) [](https://codeclimate.com/github/ryz310/redis-objects-daily-counter/test_coverage) [](https://dependabot.com)
|
2
|
+
|
3
|
+
# Redis::Objects::Daily::Counter
|
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.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'redis-objects-daily-counter'
|
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` 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
|
+
For illustration purposes, consider this stub class:
|
26
|
+
|
27
|
+
```rb
|
28
|
+
class Homepage
|
29
|
+
include Redis::Objects
|
30
|
+
|
31
|
+
daily_counter :pv, expireat: -> { Time.now + 2_678_400 } # about a month
|
32
|
+
|
33
|
+
def id
|
34
|
+
1
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# 2021-04-01
|
39
|
+
homepage = Homepage.new
|
40
|
+
homepage.id # 1
|
41
|
+
|
42
|
+
homepage.pv.increment
|
43
|
+
homepage.pv.increment
|
44
|
+
homepage.pv.increment
|
45
|
+
puts homepage.pv.value # 3
|
46
|
+
|
47
|
+
# 2021-04-02 (next day)
|
48
|
+
puts homepage.pv.value # 0
|
49
|
+
homepage.pv.increment
|
50
|
+
homepage.pv.increment
|
51
|
+
puts homepage.pv.value # 2
|
52
|
+
|
53
|
+
start_date = Date.new(2021, 4, 1)
|
54
|
+
end_date = Date.new(2021, 4, 2)
|
55
|
+
homepage.pv.range(start_date, end_date) # [3, 2]
|
56
|
+
```
|
57
|
+
|
58
|
+
The daily counter automatically switches the save destination when the date changes.
|
59
|
+
You can access past dates counted values like Ruby arrays:
|
60
|
+
|
61
|
+
```rb
|
62
|
+
# 2021-04-01
|
63
|
+
homepage.pv.increment(3)
|
64
|
+
|
65
|
+
# 2021-04-02 (next day)
|
66
|
+
homepage.pv.increment(2)
|
67
|
+
|
68
|
+
# 2021-04-03 (next day)
|
69
|
+
homepage.pv.increment(5)
|
70
|
+
|
71
|
+
homepage.pv[Date.new(2021, 4, 1)] # => 3
|
72
|
+
homepage.pv[Date.new(2021, 4, 1), 3] # => [3, 2, 5]
|
73
|
+
homepage.pv[Date.new(2021, 4, 1)..Date.new(2021, 4, 2)] # => [3, 2]
|
74
|
+
|
75
|
+
homepage.pv.delete(Date.new(2021, 4, 1))
|
76
|
+
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
|
78
|
+
```
|
79
|
+
|
80
|
+
### Counters
|
81
|
+
|
82
|
+
I recommend using with `expireat` option.
|
83
|
+
|
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`
|
97
|
+
|
98
|
+
### Timezone
|
99
|
+
|
100
|
+
This gem follows Ruby process' time zone, but if you extends Time class by ActiveSupport (e.g. `Time.current`), follows Rails process' timezone.
|
101
|
+
|
102
|
+
## Development
|
103
|
+
|
104
|
+
The development environment for this gem is configured with docker-compose.
|
105
|
+
Please use the following command:
|
106
|
+
|
107
|
+
$ docker-compose up -d
|
108
|
+
$ docker-compose run --rm ruby bundle
|
109
|
+
$ docker-compose run --rm ruby rspec .
|
110
|
+
$ docker-compose run --rm ruby rubocop -a
|
111
|
+
|
112
|
+
## Contributing
|
113
|
+
|
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).
|
115
|
+
|
116
|
+
## License
|
117
|
+
|
118
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
119
|
+
|
120
|
+
## Code of Conduct
|
121
|
+
|
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).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'redis-objects-daily-counter'
|
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.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/docker-compose.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/base_counter_object"
|
4
|
+
|
5
|
+
class Redis
|
6
|
+
class AnnualCounter < BaseCounterObject
|
7
|
+
private
|
8
|
+
|
9
|
+
def redis_daily_field_key(date_or_time)
|
10
|
+
date_key = date_or_time.strftime('%Y')
|
11
|
+
[original_key, date_key].flatten.join(':')
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_key(date, length)
|
15
|
+
date.next_year(length - 1)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
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
|
11
|
+
|
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
|
24
|
+
end
|
25
|
+
alias slice []
|
26
|
+
|
27
|
+
def delete(date_or_time)
|
28
|
+
redis.del(redis_daily_field_key(date_or_time))
|
29
|
+
end
|
30
|
+
|
31
|
+
def range(start_date, end_date)
|
32
|
+
keys = (start_date..end_date).map { |date| redis_daily_field_key(date) }.uniq
|
33
|
+
redis.mget(*keys).map(&:to_i)
|
34
|
+
end
|
35
|
+
|
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'
|
48
|
+
end
|
49
|
+
|
50
|
+
def next_key(_date, _length)
|
51
|
+
raise 'not implemented'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/base_counter_object"
|
4
|
+
|
5
|
+
class Redis
|
6
|
+
class DailyCounter < BaseCounterObject
|
7
|
+
private
|
8
|
+
|
9
|
+
def redis_daily_field_key(date_or_time)
|
10
|
+
date_key = date_or_time.strftime('%Y-%m-%d')
|
11
|
+
[original_key, date_key].flatten.join(':')
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_key(date, length)
|
15
|
+
date + length - 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/base_counter_object"
|
4
|
+
|
5
|
+
class Redis
|
6
|
+
class HourlyCounter < BaseCounterObject
|
7
|
+
def range(start_time, end_time)
|
8
|
+
keys =
|
9
|
+
(start_time.to_i..end_time.to_i)
|
10
|
+
.step(3600)
|
11
|
+
.map { |integer| redis_daily_field_key(Time.at(integer)) }
|
12
|
+
redis.mget(*keys).map(&:to_i)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def redis_daily_field_key(time)
|
18
|
+
time_key = time.strftime('%Y-%m-%dT%H')
|
19
|
+
[original_key, time_key].flatten.join(':')
|
20
|
+
end
|
21
|
+
|
22
|
+
def next_key(time, length)
|
23
|
+
time + 3600 * (length - 1)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/base_counter_object"
|
4
|
+
|
5
|
+
class Redis
|
6
|
+
class MinutelyCounter < BaseCounterObject
|
7
|
+
def range(start_time, end_time)
|
8
|
+
keys =
|
9
|
+
(start_time.to_i..end_time.to_i)
|
10
|
+
.step(60)
|
11
|
+
.map { |integer| redis_daily_field_key(Time.at(integer)) }
|
12
|
+
redis.mget(*keys).map(&:to_i)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def redis_daily_field_key(time)
|
18
|
+
time_key = time.strftime('%Y-%m-%dT%H:%M')
|
19
|
+
[original_key, time_key].flatten.join(':')
|
20
|
+
end
|
21
|
+
|
22
|
+
def next_key(time, length)
|
23
|
+
time + 60 * (length - 1)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/base_counter_object"
|
4
|
+
|
5
|
+
class Redis
|
6
|
+
class MonthlyCounter < BaseCounterObject
|
7
|
+
private
|
8
|
+
|
9
|
+
def redis_daily_field_key(date_or_time)
|
10
|
+
date_key = date_or_time.strftime('%Y-%m')
|
11
|
+
[original_key, date_key].flatten.join(':')
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_key(date, length)
|
15
|
+
date.next_month(length - 1)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis/annual_counter'
|
4
|
+
class Redis
|
5
|
+
module Objects
|
6
|
+
module AnnualCounters
|
7
|
+
class << self
|
8
|
+
def included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def annual_counter(name, options = {}) # rubocop:disable Metrics/MethodLength
|
15
|
+
options[:start] ||= 0
|
16
|
+
options[:type] ||= (options[:start]).zero? ? :increment : :decrement
|
17
|
+
redis_objects[name.to_sym] = options.merge(type: :counter)
|
18
|
+
|
19
|
+
mod = Module.new do
|
20
|
+
define_method(name) do
|
21
|
+
Redis::AnnualCounter.new(
|
22
|
+
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if options[:global]
|
28
|
+
extend mod
|
29
|
+
|
30
|
+
# dispatch to class methods
|
31
|
+
define_method(name) do
|
32
|
+
self.class.public_send(name)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
include mod
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis/daily_counter'
|
4
|
+
class Redis
|
5
|
+
module Objects
|
6
|
+
module DailyCounters
|
7
|
+
class << self
|
8
|
+
def included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def daily_counter(name, options = {}) # rubocop:disable Metrics/MethodLength
|
15
|
+
options[:start] ||= 0
|
16
|
+
options[:type] ||= (options[:start]).zero? ? :increment : :decrement
|
17
|
+
redis_objects[name.to_sym] = options.merge(type: :counter)
|
18
|
+
|
19
|
+
mod = Module.new do
|
20
|
+
define_method(name) do
|
21
|
+
Redis::DailyCounter.new(
|
22
|
+
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if options[:global]
|
28
|
+
extend mod
|
29
|
+
|
30
|
+
# dispatch to class methods
|
31
|
+
define_method(name) do
|
32
|
+
self.class.public_send(name)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
include mod
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis/hourly_counter'
|
4
|
+
class Redis
|
5
|
+
module Objects
|
6
|
+
module HourlyCounters
|
7
|
+
class << self
|
8
|
+
def included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def hourly_counter(name, options = {}) # rubocop:disable Metrics/MethodLength
|
15
|
+
options[:start] ||= 0
|
16
|
+
options[:type] ||= (options[:start]).zero? ? :increment : :decrement
|
17
|
+
redis_objects[name.to_sym] = options.merge(type: :counter)
|
18
|
+
|
19
|
+
mod = Module.new do
|
20
|
+
define_method(name) do
|
21
|
+
Redis::HourlyCounter.new(
|
22
|
+
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if options[:global]
|
28
|
+
extend mod
|
29
|
+
|
30
|
+
# dispatch to class methods
|
31
|
+
define_method(name) do
|
32
|
+
self.class.public_send(name)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
include mod
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis/minutely_counter'
|
4
|
+
class Redis
|
5
|
+
module Objects
|
6
|
+
module MinutelyCounters
|
7
|
+
class << self
|
8
|
+
def included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def minutely_counter(name, options = {}) # rubocop:disable Metrics/MethodLength
|
15
|
+
options[:start] ||= 0
|
16
|
+
options[:type] ||= (options[:start]).zero? ? :increment : :decrement
|
17
|
+
redis_objects[name.to_sym] = options.merge(type: :counter)
|
18
|
+
|
19
|
+
mod = Module.new do
|
20
|
+
define_method(name) do
|
21
|
+
Redis::MinutelyCounter.new(
|
22
|
+
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if options[:global]
|
28
|
+
extend mod
|
29
|
+
|
30
|
+
# dispatch to class methods
|
31
|
+
define_method(name) do
|
32
|
+
self.class.public_send(name)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
include mod
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis/daily_counter'
|
4
|
+
class Redis
|
5
|
+
module Objects
|
6
|
+
module MonthlyCounters
|
7
|
+
class << self
|
8
|
+
def included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def monthly_counter(name, options = {}) # rubocop:disable Metrics/MethodLength
|
15
|
+
options[:start] ||= 0
|
16
|
+
options[:type] ||= (options[:start]).zero? ? :increment : :decrement
|
17
|
+
redis_objects[name.to_sym] = options.merge(type: :counter)
|
18
|
+
|
19
|
+
mod = Module.new do
|
20
|
+
define_method(name) do
|
21
|
+
Redis::MonthlyCounter.new(
|
22
|
+
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if options[:global]
|
28
|
+
extend mod
|
29
|
+
|
30
|
+
# dispatch to class methods
|
31
|
+
define_method(name) do
|
32
|
+
self.class.public_send(name)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
include mod
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis/weekly_counter'
|
4
|
+
class Redis
|
5
|
+
module Objects
|
6
|
+
module WeeklyCounters
|
7
|
+
class << self
|
8
|
+
def included(klass)
|
9
|
+
klass.extend ClassMethods
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
def weekly_counter(name, options = {}) # rubocop:disable Metrics/MethodLength
|
15
|
+
options[:start] ||= 0
|
16
|
+
options[:type] ||= (options[:start]).zero? ? :increment : :decrement
|
17
|
+
redis_objects[name.to_sym] = options.merge(type: :counter)
|
18
|
+
|
19
|
+
mod = Module.new do
|
20
|
+
define_method(name) do
|
21
|
+
Redis::WeeklyCounter.new(
|
22
|
+
redis_field_key(name), redis_field_redis(name), redis_options(name)
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if options[:global]
|
28
|
+
extend mod
|
29
|
+
|
30
|
+
# dispatch to class methods
|
31
|
+
define_method(name) do
|
32
|
+
self.class.public_send(name)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
include mod
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "#{File.dirname(__FILE__)}/base_counter_object"
|
4
|
+
|
5
|
+
class Redis
|
6
|
+
class WeeklyCounter < BaseCounterObject
|
7
|
+
private
|
8
|
+
|
9
|
+
def redis_daily_field_key(date_or_time)
|
10
|
+
date_key = date_or_time.strftime('%YW%W')
|
11
|
+
[original_key, date_key].flatten.join(':')
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_key(date, length)
|
15
|
+
date + 7 * (length - 1)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'redis-objects'
|
4
|
+
|
5
|
+
class Redis
|
6
|
+
autoload :DailyCounter, 'redis/daily_counter'
|
7
|
+
autoload :WeeklyCounter, 'redis/weekly_counter'
|
8
|
+
autoload :MonthlyCounter, 'redis/monthly_counter'
|
9
|
+
autoload :AnnualCounter, 'redis/annual_counter'
|
10
|
+
autoload :HourlyCounter, 'redis/hourly_counter'
|
11
|
+
autoload :MinutelyCounter, 'redis/minutely_counter'
|
12
|
+
|
13
|
+
module Objects
|
14
|
+
autoload :DailyCounters, 'redis/objects/daily_counters'
|
15
|
+
autoload :WeeklyCounters, 'redis/objects/weekly_counters'
|
16
|
+
autoload :MonthlyCounters, 'redis/objects/monthly_counters'
|
17
|
+
autoload :AnnualCounters, 'redis/objects/annual_counters'
|
18
|
+
autoload :HourlyCounters, 'redis/objects/hourly_counters'
|
19
|
+
autoload :MinutelyCounters, 'redis/objects/minutely_counters'
|
20
|
+
|
21
|
+
class << self
|
22
|
+
alias original_included included
|
23
|
+
|
24
|
+
def included(klass)
|
25
|
+
original_included(klass)
|
26
|
+
|
27
|
+
# Pull in each object type
|
28
|
+
klass.send :include, Redis::Objects::DailyCounters
|
29
|
+
klass.send :include, Redis::Objects::WeeklyCounters
|
30
|
+
klass.send :include, Redis::Objects::MonthlyCounters
|
31
|
+
klass.send :include, Redis::Objects::AnnualCounters
|
32
|
+
klass.send :include, Redis::Objects::HourlyCounters
|
33
|
+
klass.send :include, Redis::Objects::MinutelyCounters
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|