redis-objects-daily-counter 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a1d7cbeced820236792c71dd5f82f33a0bcfbe9ebc897ad8001d70740ca939b
4
- data.tar.gz: 2ca71fab3345471d86ad5ce86866132fae367dfc72bd450b3f8398a40c14015b
3
+ metadata.gz: 7e0a7c068797a495a2e26fc69aa33991b83b8aaf2f61cbde250e85f79d0d2a0d
4
+ data.tar.gz: 369237147cc7c2d7edc50c0fb39845a76430d7cf30428d3d0a4a6eef51de194b
5
5
  SHA512:
6
- metadata.gz: c8810d8da10897dce9c887421acf923b2f5e4ac944c7267dd5d8df61f226dd70e5bd8da55398ada1bc0dfcb2c67c3190f8e3467ef329c2c9f226d43fac091993
7
- data.tar.gz: 6fff807b98113bfddde1a0957b8e95f371d054f920b32cff9ddf6376f1e3cefbf6f05efaed83d8193e8463eec63a64fa9d9485e43565e2c5d2026218544df0b0
6
+ metadata.gz: e41f54c69a341c6a3809c184cc20c6c0f61b0f7635048242986d5e8e4389f7118c7481a68b715c4de3d9bc7f9c05158400816fe0ec3f97ac0dab3e19598b6b4f
7
+ data.tar.gz: 2c3f6b1170202d799ed729477406179c10a60f19ea96280a269377551fda48870f3dc5912954e57cf69c6c732e027a59efbb3c96570f94ab8d93a84308c4a5fc
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2021-09-19 14:21:58 UTC using RuboCop version 0.93.1.
3
+ # on 2021-09-23 07:31:02 UTC using RuboCop version 0.93.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -11,22 +11,6 @@
11
11
  Metrics/AbcSize:
12
12
  Max: 21
13
13
 
14
- # Offense count: 21
14
+ # Offense count: 41
15
15
  Style/Documentation:
16
- Exclude:
17
- - 'spec/**/*'
18
- - 'test/**/*'
19
- - 'lib/redis-objects-daily-counter.rb'
20
- - 'lib/redis/annual_counter.rb'
21
- - 'lib/redis/base_counter_object.rb'
22
- - 'lib/redis/daily_counter.rb'
23
- - 'lib/redis/hourly_counter.rb'
24
- - 'lib/redis/minutely_counter.rb'
25
- - 'lib/redis/monthly_counter.rb'
26
- - 'lib/redis/objects/annual_counters.rb'
27
- - 'lib/redis/objects/daily_counters.rb'
28
- - 'lib/redis/objects/hourly_counters.rb'
29
- - 'lib/redis/objects/minutely_counters.rb'
30
- - 'lib/redis/objects/monthly_counters.rb'
31
- - 'lib/redis/objects/weekly_counters.rb'
32
- - 'lib/redis/weekly_counter.rb'
16
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,74 @@
1
1
  # Change log
2
2
 
3
+ ## v0.3.0 (Sep 23, 2021)
4
+
5
+ ### Feature
6
+
7
+ * [#7](https://github.com/ryz310/redis-object-daily-counter/pull/7) Add daily set ([@ryz310](https://github.com/ryz310))
8
+
9
+ > You can use `daily_set` in addition to the standard features of Redis::Objects.
10
+ >
11
+ > ```rb
12
+ > class Homepage
13
+ > include Redis::Objects
14
+ >
15
+ > daily_set :dau, expireat: -> { Time.now + 2_678_400 } # about a month
16
+ >
17
+ > def id
18
+ > 1
19
+ > end
20
+ > end
21
+ >
22
+ > # 2021-04-01
23
+ > homepage.dau << 'user1'
24
+ > homepage.dau << 'user2'
25
+ > homepage.dau << 'user1' # dup ignored
26
+ > puts homepage.dau.members # ['user1', 'user2']
27
+ > puts homepage.dau.length # 2
28
+ > puts homepage.dau.count # alias of #length
29
+ >
30
+ > # 2021-04-02 (next day)
31
+ > puts homepage.dau.members # []
32
+ > homepage.dau.merge('user2', 'user3')
33
+ > puts homepage.dau.members # ['user2', 'user3']
34
+ >
35
+ > # 2021-04-03 (next day)
36
+ > homepage.dau.merge('user4')
37
+ >
38
+ > homepage.dau[Date.new(2021, 4, 1)] # => ['user1', 'user2']
39
+ > homepage.dau[Date.new(2021, 4, 1), 3] # => ['user1', 'user2', 'user3', 'user4']
40
+ > homepage.dau[Date.new(2021, 4, 1)..Date.new(2021, 4, 2)] # => ['user1', 'user2', 'user3']
41
+ >
42
+ > homepage.dau.delete_at(Date.new(2021, 4, 1))
43
+ > homepage.dau.range(Date.new(2021, 4, 1), Date.new(2021, 4, 3)) # => ['user2', 'user3', 'user4']
44
+ > homepage.dau.at(Date.new(2021, 4, 2)) # => #<Redis::Set key="homepage:1:dau:2021-04-02">
45
+ > homepage.dau.at(Date.new(2021, 4, 2)).members # ['user2', 'user3']
46
+ > ```
47
+
48
+ ### Breaking Change
49
+
50
+ * [#7](https://github.com/ryz310/redis-object-daily-counter/pull/7) Add daily set ([@ryz310](https://github.com/ryz310))
51
+
52
+ > Rename the method from #delete to #delete_at a73251f
53
+ >
54
+ > ```rb
55
+ > # Before
56
+ > homepage.pv.delete(Date.new(2021, 4, 1))
57
+ >
58
+ > # After
59
+ > homepage.pv.delete_at(Date.new(2021, 4, 1))
60
+ > ```
61
+ >
62
+ > Modify returning value of RecurringAtIntervals#at 1c8cc79
63
+ >
64
+ > ```rb
65
+ > # Before
66
+ > homepage.pv.at(Date.new(2021, 4, 2)) # => 2
67
+ >
68
+ > # After
69
+ > homepage.pv.at(Date.new(2021, 4, 2)) # => #<Redis::Counter key="homepage:1:pv:2021-04-02">
70
+ > ```
71
+
3
72
  ## v0.2.0 (Sep 20, 2021)
4
73
 
5
74
  ### Feature
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- redis-objects-daily-counter (0.2.0)
4
+ redis-objects-daily-counter (0.3.0)
5
5
  redis-objects
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,8 +1,8 @@
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-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/1e1cb0d70e4f80e0fdd5/maintainability)](https://codeclimate.com/github/ryz310/redis-objects-daily-counter/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/1e1cb0d70e4f80e0fdd5/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)
2
2
 
3
- # Redis::Objects::Daily::Counter
3
+ # Redis::Objects::Daily::Counter and Daily::Set
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 daily counter, the daily 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
 
@@ -16,12 +16,13 @@ If you want to know about installation and standard usage, please see Redis::Obj
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,7 @@ 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_set :dau, expireat: -> { Time.now + 2_678_400 } # about a month
32
34
 
33
35
  def id
34
36
  1
@@ -55,6 +57,8 @@ end_date = Date.new(2021, 4, 2)
55
57
  homepage.pv.range(start_date, end_date) # [3, 2]
56
58
  ```
57
59
 
60
+ ### Daily Counter
61
+
58
62
  The daily counter automatically switches the save destination when the date changes.
59
63
  You can access past dates counted values like Ruby arrays:
60
64
 
@@ -72,14 +76,13 @@ homepage.pv[Date.new(2021, 4, 1)] # => 3
72
76
  homepage.pv[Date.new(2021, 4, 1), 3] # => [3, 2, 5]
73
77
  homepage.pv[Date.new(2021, 4, 1)..Date.new(2021, 4, 2)] # => [3, 2]
74
78
 
75
- homepage.pv.delete(Date.new(2021, 4, 1))
79
+ homepage.pv.delete_at(Date.new(2021, 4, 1))
76
80
  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
81
+ homepage.pv.at(Date.new(2021, 4, 2)) # => #<Redis::Counter key="homepage:1:pv:2021-04-02">
82
+ homepage.pv.at(Date.new(2021, 4, 2)).value # 2
78
83
  ```
79
84
 
80
- ### Counters
81
-
82
- I recommend using with `expireat` option.
85
+ #### Daily Counter Family
83
86
 
84
87
  * `annual_counter`
85
88
  * Key format: `model_name:id:field_name:yyyy`
@@ -95,6 +98,55 @@ I recommend using with `expireat` option.
95
98
  * `minutely_counter`
96
99
  * Key format: `model_name:id:field_name:yyyy-mm-ddThh:mi`
97
100
 
101
+ ### Daily Set
102
+
103
+ The daily set also automatically switches the save destination when the date changes.
104
+
105
+ ```rb
106
+ # 2021-04-01
107
+ homepage.dau << 'user1'
108
+ homepage.dau << 'user2'
109
+ homepage.dau << 'user1' # dup ignored
110
+ puts homepage.dau.members # ['user1', 'user2']
111
+ puts homepage.dau.length # 2
112
+ puts homepage.dau.count # alias of #length
113
+
114
+ # 2021-04-02 (next day)
115
+ puts homepage.dau.members # []
116
+
117
+ homepage.dau.merge('user2', 'user3')
118
+ puts homepage.dau.members # ['user2', 'user3']
119
+
120
+ # 2021-04-03 (next day)
121
+ homepage.dau.merge('user4')
122
+
123
+ homepage.dau[Date.new(2021, 4, 1)] # => ['user1', 'user2']
124
+ homepage.dau[Date.new(2021, 4, 1), 3] # => ['user1', 'user2', 'user3', 'user4']
125
+ homepage.dau[Date.new(2021, 4, 1)..Date.new(2021, 4, 2)] # => ['user1', 'user2', 'user3']
126
+
127
+ homepage.dau.delete_at(Date.new(2021, 4, 1))
128
+ homepage.dau.range(Date.new(2021, 4, 1), Date.new(2021, 4, 3)) # => ['user2', 'user3', 'user4']
129
+ homepage.dau.at(Date.new(2021, 4, 2)) # => #<Redis::Set key="homepage:1:dau:2021-04-02">
130
+ homepage.dau.at(Date.new(2021, 4, 2)).members # ['user2', 'user3']
131
+ ```
132
+
133
+ #### Daily Set Family
134
+
135
+ * `annual_set`
136
+ * Key format: `model_name:id:field_name:yyyy`
137
+ * Redis is a highly volatile key-value store, so I don't recommend using it.
138
+ * `monthly_set`
139
+ * Key format: `model_name:id:field_name:yyyy-mm`
140
+ * `weekly_set`
141
+ * Key format: `model_name:id:field_name:yyyyWw`
142
+ * `daily_set`
143
+ * Key format: `model_name:id:field_name:yyyy-mm-dd`
144
+ * `hourly_set`
145
+ * Key format: `model_name:id:field_name:yyyy-mm-ddThh`
146
+ * `minutely_set`
147
+ * Key format: `model_name:id:field_name:yyyy-mm-ddThh:mi`
148
+
149
+
98
150
  ### Timezone
99
151
 
100
152
  This gem follows Ruby process' time zone, but if you extends Time class by ActiveSupport (e.g. `Time.current`), follows Rails process' timezone.
@@ -1,18 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
3
4
  require "#{File.dirname(__FILE__)}/base_counter_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/annual"
4
6
 
5
7
  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
8
+ class AnnualCounter < Counter
9
+ include RecurringAtIntervals
10
+ include BaseCounterObject
11
+ include Annual
17
12
  end
18
13
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
4
+ require "#{File.dirname(__FILE__)}/base_set_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/annual"
6
+
7
+ class Redis
8
+ class AnnualSet < Set
9
+ include RecurringAtIntervals
10
+ include BaseSetObject
11
+ include Annual
12
+ end
13
+ end
@@ -1,54 +1,23 @@
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'
48
- end
49
-
50
- def next_key(_date, _length)
51
- raise 'not implemented'
19
+ def delete_from_redis(key)
20
+ redis.del(key)
52
21
  end
53
22
  end
54
23
  end
@@ -0,0 +1,24 @@
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
+ end
24
+ end
@@ -1,18 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
3
4
  require "#{File.dirname(__FILE__)}/base_counter_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/daily"
4
6
 
5
7
  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
8
+ class DailyCounter < Counter
9
+ include RecurringAtIntervals
10
+ include BaseCounterObject
11
+ include Daily
17
12
  end
18
13
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
4
+ require "#{File.dirname(__FILE__)}/base_set_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/daily"
6
+
7
+ class Redis
8
+ class DailySet < Set
9
+ include RecurringAtIntervals
10
+ include BaseSetObject
11
+ include Daily
12
+ end
13
+ end
@@ -1,26 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
3
4
  require "#{File.dirname(__FILE__)}/base_counter_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/hourly"
4
6
 
5
7
  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
8
+ class HourlyCounter < Counter
9
+ include RecurringAtIntervals
10
+ include BaseCounterObject
11
+ include Hourly
25
12
  end
26
13
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
4
+ require "#{File.dirname(__FILE__)}/base_set_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/hourly"
6
+
7
+ class Redis
8
+ class HourlySet < Set
9
+ include RecurringAtIntervals
10
+ include BaseSetObject
11
+ include Hourly
12
+ end
13
+ end
@@ -1,26 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
3
4
  require "#{File.dirname(__FILE__)}/base_counter_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/minutely"
4
6
 
5
7
  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
8
+ class MinutelyCounter < Counter
9
+ include RecurringAtIntervals
10
+ include BaseCounterObject
11
+ include Minutely
25
12
  end
26
13
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
4
+ require "#{File.dirname(__FILE__)}/base_set_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/minutely"
6
+
7
+ class Redis
8
+ class MinutelySet < Set
9
+ include RecurringAtIntervals
10
+ include BaseSetObject
11
+ include Minutely
12
+ end
13
+ end
@@ -1,18 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
3
4
  require "#{File.dirname(__FILE__)}/base_counter_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/monthly"
4
6
 
5
7
  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
8
+ class MonthlyCounter < Counter
9
+ include RecurringAtIntervals
10
+ include BaseCounterObject
11
+ include Monthly
17
12
  end
18
13
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
4
+ require "#{File.dirname(__FILE__)}/base_set_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/monthly"
6
+
7
+ class Redis
8
+ class MonthlySet < Set
9
+ include RecurringAtIntervals
10
+ include BaseSetObject
11
+ include Monthly
12
+ end
13
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis/annual_set'
4
+ class Redis
5
+ module Objects
6
+ module AnnualSets
7
+ def self.included(klass)
8
+ klass.extend ClassMethods
9
+ end
10
+
11
+ # Class methods that appear in your class when you include Redis::Objects.
12
+ module ClassMethods
13
+ # Define a new list. It will function like a regular instance
14
+ # method, so it can be used alongside ActiveRecord, DataMapper, etc.
15
+ def annual_set(name, options = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
16
+ redis_objects[name.to_sym] = options.merge(type: :set)
17
+
18
+ mod = Module.new do
19
+ define_method(name) do
20
+ Redis::AnnualSet.new(
21
+ redis_field_key(name), redis_field_redis(name), redis_options(name)
22
+ )
23
+ end
24
+
25
+ define_method(:"#{name}=") do |values|
26
+ set = public_send(name)
27
+
28
+ redis.pipelined do
29
+ set.clear
30
+ set.merge(*values)
31
+ end
32
+ end
33
+ end
34
+
35
+ if options[:global]
36
+ extend mod
37
+
38
+ # dispatch to class methods
39
+ define_method(name) do
40
+ self.class.public_send(name)
41
+ end
42
+ else
43
+ include mod
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -4,7 +4,7 @@ class Redis
4
4
  module Objects
5
5
  module Daily
6
6
  module Counter
7
- VERSION = '0.2.0'
7
+ VERSION = '0.3.0'
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis/daily_set'
4
+ class Redis
5
+ module Objects
6
+ module DailySets
7
+ def self.included(klass)
8
+ klass.extend ClassMethods
9
+ end
10
+
11
+ # Class methods that appear in your class when you include Redis::Objects.
12
+ module ClassMethods
13
+ # Define a new list. It will function like a regular instance
14
+ # method, so it can be used alongside ActiveRecord, DataMapper, etc.
15
+ def daily_set(name, options = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
16
+ redis_objects[name.to_sym] = options.merge(type: :set)
17
+
18
+ mod = Module.new do
19
+ define_method(name) do
20
+ Redis::DailySet.new(
21
+ redis_field_key(name), redis_field_redis(name), redis_options(name)
22
+ )
23
+ end
24
+
25
+ define_method(:"#{name}=") do |values|
26
+ set = public_send(name)
27
+
28
+ redis.pipelined do
29
+ set.clear
30
+ set.merge(*values)
31
+ end
32
+ end
33
+ end
34
+
35
+ if options[:global]
36
+ extend mod
37
+
38
+ # dispatch to class methods
39
+ define_method(name) do
40
+ self.class.public_send(name)
41
+ end
42
+ else
43
+ include mod
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis/hourly_set'
4
+ class Redis
5
+ module Objects
6
+ module HourlySets
7
+ def self.included(klass)
8
+ klass.extend ClassMethods
9
+ end
10
+
11
+ # Class methods that appear in your class when you include Redis::Objects.
12
+ module ClassMethods
13
+ # Define a new list. It will function like a regular instance
14
+ # method, so it can be used alongside ActiveRecord, DataMapper, etc.
15
+ def hourly_set(name, options = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
16
+ redis_objects[name.to_sym] = options.merge(type: :set)
17
+
18
+ mod = Module.new do
19
+ define_method(name) do
20
+ Redis::HourlySet.new(
21
+ redis_field_key(name), redis_field_redis(name), redis_options(name)
22
+ )
23
+ end
24
+
25
+ define_method(:"#{name}=") do |values|
26
+ set = public_send(name)
27
+
28
+ redis.pipelined do
29
+ set.clear
30
+ set.merge(*values)
31
+ end
32
+ end
33
+ end
34
+
35
+ if options[:global]
36
+ extend mod
37
+
38
+ # dispatch to class methods
39
+ define_method(name) do
40
+ self.class.public_send(name)
41
+ end
42
+ else
43
+ include mod
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis/minutely_set'
4
+ class Redis
5
+ module Objects
6
+ module MinutelySets
7
+ def self.included(klass)
8
+ klass.extend ClassMethods
9
+ end
10
+
11
+ # Class methods that appear in your class when you include Redis::Objects.
12
+ module ClassMethods
13
+ # Define a new list. It will function like a regular instance
14
+ # method, so it can be used alongside ActiveRecord, DataMapper, etc.
15
+ def minutely_set(name, options = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
16
+ redis_objects[name.to_sym] = options.merge(type: :set)
17
+
18
+ mod = Module.new do
19
+ define_method(name) do
20
+ Redis::MinutelySet.new(
21
+ redis_field_key(name), redis_field_redis(name), redis_options(name)
22
+ )
23
+ end
24
+
25
+ define_method(:"#{name}=") do |values|
26
+ set = public_send(name)
27
+
28
+ redis.pipelined do
29
+ set.clear
30
+ set.merge(*values)
31
+ end
32
+ end
33
+ end
34
+
35
+ if options[:global]
36
+ extend mod
37
+
38
+ # dispatch to class methods
39
+ define_method(name) do
40
+ self.class.public_send(name)
41
+ end
42
+ else
43
+ include mod
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis/monthly_set'
4
+ class Redis
5
+ module Objects
6
+ module MonthlySets
7
+ def self.included(klass)
8
+ klass.extend ClassMethods
9
+ end
10
+
11
+ # Class methods that appear in your class when you include Redis::Objects.
12
+ module ClassMethods
13
+ # Define a new list. It will function like a regular instance
14
+ # method, so it can be used alongside ActiveRecord, DataMapper, etc.
15
+ def monthly_set(name, options = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
16
+ redis_objects[name.to_sym] = options.merge(type: :set)
17
+
18
+ mod = Module.new do
19
+ define_method(name) do
20
+ Redis::MonthlySet.new(
21
+ redis_field_key(name), redis_field_redis(name), redis_options(name)
22
+ )
23
+ end
24
+
25
+ define_method(:"#{name}=") do |values|
26
+ set = public_send(name)
27
+
28
+ redis.pipelined do
29
+ set.clear
30
+ set.merge(*values)
31
+ end
32
+ end
33
+ end
34
+
35
+ if options[:global]
36
+ extend mod
37
+
38
+ # dispatch to class methods
39
+ define_method(name) do
40
+ self.class.public_send(name)
41
+ end
42
+ else
43
+ include mod
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis/weekly_set'
4
+ class Redis
5
+ module Objects
6
+ module WeeklySets
7
+ def self.included(klass)
8
+ klass.extend ClassMethods
9
+ end
10
+
11
+ # Class methods that appear in your class when you include Redis::Objects.
12
+ module ClassMethods
13
+ # Define a new list. It will function like a regular instance
14
+ # method, so it can be used alongside ActiveRecord, DataMapper, etc.
15
+ def weekly_set(name, options = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
16
+ redis_objects[name.to_sym] = options.merge(type: :set)
17
+
18
+ mod = Module.new do
19
+ define_method(name) do
20
+ Redis::WeeklySet.new(
21
+ redis_field_key(name), redis_field_redis(name), redis_options(name)
22
+ )
23
+ end
24
+
25
+ define_method(:"#{name}=") do |values|
26
+ set = public_send(name)
27
+
28
+ redis.pipelined do
29
+ set.clear
30
+ set.merge(*values)
31
+ end
32
+ end
33
+ end
34
+
35
+ if options[:global]
36
+ extend mod
37
+
38
+ # dispatch to class methods
39
+ define_method(name) do
40
+ self.class.public_send(name)
41
+ end
42
+ else
43
+ include mod
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ 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
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module RecurringAtIntervals
5
+ module Weekly
6
+ private
7
+
8
+ def redis_daily_field_key(date_or_time)
9
+ date_key = date_or_time.strftime('%YW%W')
10
+ [original_key, date_key].flatten.join(':')
11
+ end
12
+
13
+ def next_key(date, length = 1)
14
+ date + 7 * length
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module RecurringAtIntervals
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 - 1))
18
+ when 0 then []
19
+ when -1 then nil # Ruby does this (a bit weird)
20
+ end
21
+ else
22
+ get_value_from_redis(redis_daily_field_key(date_or_time))
23
+ end
24
+ end
25
+ alias slice []
26
+
27
+ def delete_at(date_or_time)
28
+ delete_from_redis(redis_daily_field_key(date_or_time))
29
+ end
30
+
31
+ def range(start_date_or_time, end_date_or_time)
32
+ keys = []
33
+ date_or_time = start_date_or_time
34
+
35
+ loop do
36
+ break if date_or_time > end_date_or_time
37
+
38
+ keys << redis_daily_field_key(date_or_time)
39
+ date_or_time = next_key(date_or_time)
40
+ end
41
+
42
+ get_values_from_redis(keys)
43
+ end
44
+
45
+ def at(date_or_time)
46
+ get_redis_object(redis_daily_field_key(date_or_time))
47
+ end
48
+
49
+ def current_time
50
+ @current_time ||= Time.respond_to?(:current) ? Time.current : Time.now
51
+ end
52
+
53
+ private
54
+
55
+ def get_redis_object(_key)
56
+ raise 'not implemented'
57
+ end
58
+
59
+ def get_value_from_redis(_key)
60
+ raise 'not implemented'
61
+ end
62
+
63
+ def get_values_from_redis(_keys)
64
+ raise 'not implemented'
65
+ end
66
+
67
+ def delete_from_redis(_key)
68
+ raise 'not implemented'
69
+ end
70
+
71
+ def redis_daily_field_key(_date_or_time)
72
+ raise 'not implemented'
73
+ end
74
+
75
+ def next_key(_date, _length = 1)
76
+ raise 'not implemented'
77
+ end
78
+ end
79
+ end
@@ -1,18 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
3
4
  require "#{File.dirname(__FILE__)}/base_counter_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/weekly"
4
6
 
5
7
  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
8
+ class WeeklyCounter < Counter
9
+ include RecurringAtIntervals
10
+ include BaseCounterObject
11
+ include Weekly
17
12
  end
18
13
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals"
4
+ require "#{File.dirname(__FILE__)}/base_set_object"
5
+ require "#{File.dirname(__FILE__)}/recurring_at_intervals/weekly"
6
+
7
+ class Redis
8
+ class WeeklySet < Set
9
+ include RecurringAtIntervals
10
+ include BaseSetObject
11
+ include Weekly
12
+ end
13
+ end
@@ -10,6 +10,13 @@ class Redis
10
10
  autoload :HourlyCounter, 'redis/hourly_counter'
11
11
  autoload :MinutelyCounter, 'redis/minutely_counter'
12
12
 
13
+ autoload :DailySet, 'redis/daily_set'
14
+ autoload :WeeklySet, 'redis/weekly_set'
15
+ autoload :MonthlySet, 'redis/monthly_set'
16
+ autoload :AnnualSet, 'redis/annual_set'
17
+ autoload :HourlySet, 'redis/hourly_set'
18
+ autoload :MinutelySet, 'redis/minutely_set'
19
+
13
20
  module Objects
14
21
  autoload :DailyCounters, 'redis/objects/daily_counters'
15
22
  autoload :WeeklyCounters, 'redis/objects/weekly_counters'
@@ -18,6 +25,13 @@ class Redis
18
25
  autoload :HourlyCounters, 'redis/objects/hourly_counters'
19
26
  autoload :MinutelyCounters, 'redis/objects/minutely_counters'
20
27
 
28
+ autoload :DailySets, 'redis/objects/daily_sets'
29
+ autoload :WeeklySets, 'redis/objects/weekly_sets'
30
+ autoload :MonthlySets, 'redis/objects/monthly_sets'
31
+ autoload :AnnualSets, 'redis/objects/annual_sets'
32
+ autoload :HourlySets, 'redis/objects/hourly_sets'
33
+ autoload :MinutelySets, 'redis/objects/minutely_sets'
34
+
21
35
  class << self
22
36
  alias original_included included
23
37
 
@@ -31,6 +45,13 @@ class Redis
31
45
  klass.send :include, Redis::Objects::AnnualCounters
32
46
  klass.send :include, Redis::Objects::HourlyCounters
33
47
  klass.send :include, Redis::Objects::MinutelyCounters
48
+
49
+ klass.send :include, Redis::Objects::DailySets
50
+ klass.send :include, Redis::Objects::WeeklySets
51
+ klass.send :include, Redis::Objects::MonthlySets
52
+ klass.send :include, Redis::Objects::AnnualSets
53
+ klass.send :include, Redis::Objects::HourlySets
54
+ klass.send :include, Redis::Objects::MinutelySets
34
55
  end
35
56
  end
36
57
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-objects-daily-counter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ryz310
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-20 00:00:00.000000000 Z
11
+ date: 2021-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-objects
@@ -50,19 +50,39 @@ files:
50
50
  - docker-compose.yml
51
51
  - lib/redis-objects-daily-counter.rb
52
52
  - lib/redis/annual_counter.rb
53
+ - lib/redis/annual_set.rb
53
54
  - lib/redis/base_counter_object.rb
55
+ - lib/redis/base_set_object.rb
54
56
  - lib/redis/daily_counter.rb
57
+ - lib/redis/daily_set.rb
55
58
  - lib/redis/hourly_counter.rb
59
+ - lib/redis/hourly_set.rb
56
60
  - lib/redis/minutely_counter.rb
61
+ - lib/redis/minutely_set.rb
57
62
  - lib/redis/monthly_counter.rb
63
+ - lib/redis/monthly_set.rb
58
64
  - lib/redis/objects/annual_counters.rb
65
+ - lib/redis/objects/annual_sets.rb
59
66
  - lib/redis/objects/daily-counter/version.rb
60
67
  - lib/redis/objects/daily_counters.rb
68
+ - lib/redis/objects/daily_sets.rb
61
69
  - lib/redis/objects/hourly_counters.rb
70
+ - lib/redis/objects/hourly_sets.rb
62
71
  - lib/redis/objects/minutely_counters.rb
72
+ - lib/redis/objects/minutely_sets.rb
63
73
  - lib/redis/objects/monthly_counters.rb
74
+ - lib/redis/objects/monthly_sets.rb
64
75
  - lib/redis/objects/weekly_counters.rb
76
+ - lib/redis/objects/weekly_sets.rb
77
+ - lib/redis/recurring_at_intervals.rb
78
+ - lib/redis/recurring_at_intervals/annual.rb
79
+ - lib/redis/recurring_at_intervals/daily.rb
80
+ - lib/redis/recurring_at_intervals/hourly.rb
81
+ - lib/redis/recurring_at_intervals/minutely.rb
82
+ - lib/redis/recurring_at_intervals/monthly.rb
83
+ - lib/redis/recurring_at_intervals/weekly.rb
65
84
  - lib/redis/weekly_counter.rb
85
+ - lib/redis/weekly_set.rb
66
86
  - redis-objects-daily-counter.gemspec
67
87
  homepage: https://github.com/ryz310/redis-objects-daily-counter
68
88
  licenses:
@@ -71,7 +91,7 @@ metadata:
71
91
  homepage_uri: https://github.com/ryz310/redis-objects-daily-counter
72
92
  source_code_uri: https://github.com/ryz310/redis-objects-daily-counter
73
93
  changelog_uri: https://github.com/ryz310/redis-objects-daily-counter/blob/master/CHANGELOG.md
74
- post_install_message:
94
+ post_install_message:
75
95
  rdoc_options: []
76
96
  require_paths:
77
97
  - lib
@@ -86,8 +106,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
106
  - !ruby/object:Gem::Version
87
107
  version: '0'
88
108
  requirements: []
89
- rubygems_version: 3.2.3
90
- signing_key:
109
+ rubygems_version: 3.2.22
110
+ signing_key:
91
111
  specification_version: 4
92
112
  summary: Daily counter within Redis::Objects
93
113
  test_files: []