redis-objects-daily-counter 0.2.0 → 0.3.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 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: []