rate-limit 0.0.1 → 0.1.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: 9b6d225525d8976ef1c3c8e7d0fdb1c71a48071357cff8f64633117a13ab714e
4
- data.tar.gz: 057bf093d1271c83795c34472cf31e273353a24621bddebb86a901b48d451e8b
3
+ metadata.gz: 4b73af03f6ea918dd965b1f1d89b8f7131e1fbba2b94166a397c100510c4a426
4
+ data.tar.gz: fcd12f564f38c214a088032b9ee912337dac4a566a1cd97c39256180e2e8e8a5
5
5
  SHA512:
6
- metadata.gz: 2e6a6fe077419b22d5dcc5357fdf127ab7c944aff6e1bf9d44ece39cf56f1fc1b6b12ac400aa1a2ffdeab9964f90f7739952cda796b3f23a82748410c64b324b
7
- data.tar.gz: 19f68ffd995fca03b3c6787a208649b2c11c2206dd598a6b82feea25b51451817a3f354ca5de7fd9435a6fb6a61b8ac763db37a9dc2535425627eeccfc976333
6
+ metadata.gz: 2a9e190cda1984c96ffd6d93751e56a323d783778c170a492d0b67b151d4a85ef83c88907526f48dc0cc574bdb19880a09667f2472b4e5873d54262e8b7dd976
7
+ data.tar.gz: b0529be97de2244366f3ac99f6192e7e649ff8574e15b44f51e2d56030dbcb9163238a1a03d77ed3b49d67c5669cba865c674216aa83dfe73877ab837aae02dd
data/CHANGELOG.md CHANGED
@@ -0,0 +1,43 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog],
6
+ and this project adheres to [Semantic Versioning].
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [v0.1.0] - 2022-09-16
11
+
12
+
13
+ ### Added
14
+
15
+ - [https://github.com/catawiki/rate-limit/pull/11] `RateLimit::Result` class
16
+ - [https://github.com/catawiki/rate-limit/pull/12] `RateLimit::Worker` class
17
+ - [https://github.com/catawiki/rate-limit/pull/13] `RateLimit::Config#on_success` and `RateLimit::Config#on_failure`
18
+
19
+ ### Changed
20
+
21
+ - `RateLimit.throttle` to not accept block
22
+ - `RateLimit.throttle` to return `RateLimit::Result` object
23
+ - `RateLimit::Throttler` from class to module while moving responsibilities to `RateLimit::Worker` class
24
+ - renamed `RateLimit.throttle!` to `RateLimit.throttle_with_block!`
25
+ - renamed `RateLimit.throttle_only_failures` to `RateLimit.throttle_only_failures_with_block!`
26
+
27
+ ### Fixed
28
+
29
+ - [https://github.com/catawiki/rate-limit/issues/7] Symbol topic names does not load the correct limits
30
+ - [https://github.com/catawiki/rate-limit/issues/6] Main Module (RateLimit) fails to autoload
31
+
32
+
33
+ ## v0.0.1 - 2022-09-09
34
+
35
+ - Initial gem release
36
+
37
+ [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
38
+ [Semantic Versioning]: https://semver.org/spec/v2.0.0.html
39
+
40
+ <!-- versions -->
41
+
42
+ [Unreleased]: https://github.com/catawiki/rate-limit/compare/v0.1.0...HEAD
43
+ [v0.1.0]: https://github.com/catawiki/rate-limit/compare/v0.0.1...v0.1.0
data/README.md CHANGED
@@ -33,14 +33,18 @@ Or install it yourself as:
33
33
  #### Basic `RateLimit.throttle`
34
34
 
35
35
  ```ruby
36
- if RateLimit.throttle(topic: :login, namespace: :user_id, value: id)
36
+ result = RateLimit.throttle(topic: :login, namespace: :user_id, value: id)
37
+
38
+ if result.success?
37
39
  # Do something
38
40
  end
39
41
  ```
40
42
  or
41
43
 
42
44
  ```ruby
43
- if RateLimit.throttle(topic: :login, value: id)
45
+ result = RateLimit.throttle(topic: :login, namespace: :user_id, value: id)
46
+
47
+ if result.success?
44
48
  # Do something
45
49
  end
46
50
  ```
@@ -49,7 +53,7 @@ end
49
53
 
50
54
  ```ruby
51
55
  begin
52
- RateLimit.throttle!(topic: :send_sms, namespace: :user_id, value: id) do
56
+ RateLimit.throttle_with_block!(topic: :send_sms, namespace: :user_id, value: id) do
53
57
  # Logic goes Here
54
58
  end
55
59
  rescue RateLimit::Errors::LimitExceededError => e
@@ -65,10 +69,10 @@ end
65
69
  #### Advanced
66
70
 
67
71
  ```ruby
68
- throttler = RateLimit::Throttler.new(topic: :login, namespace: :user_id, value: id)
72
+ throttler = RateLimit::Worker.new(topic: :login, namespace: :user_id, value: id)
69
73
 
70
74
  begin
71
- throttler.perform! do
75
+ throttler.throttle_with_block! do
72
76
  # Logic goes Here
73
77
  end
74
78
  rescue RateLimit::Errors::LimitExceededError => e
@@ -79,7 +83,7 @@ end
79
83
  #### Manual
80
84
 
81
85
  ```ruby
82
- throttler = RateLimit::Throttler.new(topic: :login, namespace: :user_id, value: id)
86
+ throttler = RateLimit::Worker.new(topic: :login, namespace: :user_id, value: id)
83
87
 
84
88
  unless throttler.limit_exceeded?
85
89
  # Logic goes Here
@@ -92,8 +96,8 @@ end
92
96
 
93
97
  ```ruby
94
98
  begin
95
- RateLimit.throttle!(topic: :send_sms, namespace: :user_id, value: id) do
96
- RateLimit.throttle!(topic: :send_sms, namespace: :phone_number, value: number) do
99
+ RateLimit.throttle_with_block!(topic: :send_sms, namespace: :user_id, value: id) do
100
+ RateLimit.throttle_with_block!(topic: :send_sms, namespace: :phone_number, value: number) do
97
101
  # Logic goes Here
98
102
  end
99
103
  end
@@ -113,6 +117,14 @@ RateLimit.configure do |config|
113
117
  config.default_interval = 60
114
118
  config.default_threshold = 2
115
119
  config.limits_file_path = 'config/rate-limit.yml'
120
+ config.on_success = proc { |result|
121
+ # Success Logic Goes HERE
122
+ # result.topic, result.namespace, result.value
123
+ }
124
+ config.on_failure = proc { |result|
125
+ # Failure Logic Goes HERE
126
+ # result.topic, result.namespace, result.value, result.threshold, result.interval
127
+ }
116
128
  end
117
129
  ```
118
130
 
data/lib/rate-limit.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rate_limit'
@@ -3,29 +3,35 @@
3
3
  module RateLimit
4
4
  module Base
5
5
  def throttle(**args)
6
- throttle!(**args) { yield if block_given? }
7
- rescue Errors::LimitExceededError => _e
8
- false
6
+ worker = Worker.new(**args)
7
+ worker.throttle
8
+ worker.result
9
9
  end
10
10
 
11
- def throttle!(**args)
12
- Throttler.new(**args).perform! { yield if block_given? }
11
+ def throttle_with_block!(**args, &block)
12
+ worker = Worker.new(**args)
13
+
14
+ worker.throttle_with_block!(&block)
15
+ worker.result
13
16
  end
14
17
 
15
- def throttle_only_failures!(**args)
16
- Throttler.new(**args).perform_only_failures! { yield if block_given? }
18
+ def throttle_only_failures_with_block!(**args, &block)
19
+ worker = Worker.new(**args)
20
+
21
+ worker.throttle_only_failures_with_block!(&block)
22
+ worker.result
17
23
  end
18
24
 
19
25
  def limit_exceeded?(**args)
20
- Throttler.new(**args).limit_exceeded?
26
+ Worker.new(**args).reloaded_limit_exceeded?
21
27
  end
22
28
 
23
29
  def reset_counters(**args)
24
- Throttler.new(**args).clear_cache_counter
30
+ Worker.new(**args).clear_cache_counter
25
31
  end
26
32
 
27
33
  def increment_counters(**args)
28
- Throttler.new(**args).increment_cache_counter
34
+ Worker.new(**args).increment_cache_counter
29
35
  end
30
36
  end
31
37
  end
@@ -9,6 +9,8 @@ module RateLimit
9
9
  attr_accessor :default_interval,
10
10
  :default_threshold,
11
11
  :limits_file_path,
12
+ :on_success,
13
+ :on_failure,
12
14
  :fail_safe,
13
15
  :redis
14
16
 
@@ -24,6 +26,14 @@ module RateLimit
24
26
  raw_limits[topic] || Defaults.raw_limits
25
27
  end
26
28
 
29
+ def success_callback(*args)
30
+ on_success&.call(*args)
31
+ end
32
+
33
+ def failure_callback(*args)
34
+ on_failure&.call(*args)
35
+ end
36
+
27
37
  private
28
38
 
29
39
  def raw_limits
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RateLimit
4
+ class Result
5
+ # Attributes
6
+ attr_accessor :topic, :namespace, :value, :threshold, :interval
7
+
8
+ # Methods
9
+ def initialize(worker, success)
10
+ @topic = worker.topic
11
+ @namespace = worker.namespace
12
+ @value = worker.value
13
+ @threshold = worker.exceeded_window&.threshold
14
+ @interval = worker.exceeded_window&.interval
15
+ @success = success
16
+ end
17
+
18
+ def success?
19
+ @success
20
+ end
21
+ end
22
+ end
@@ -1,57 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RateLimit
4
- class Throttler
5
- attr_accessor :topic, :namespace, :value, :limits, :windows
4
+ module Throttler
5
+ def throttle
6
+ return failure! if reloaded_limit_exceeded?
6
7
 
7
- def initialize(topic:, value:, namespace: nil)
8
- @topic = topic.to_s
9
- @value = value.to_i
10
- @namespace = namespace&.to_s
11
- @windows = Limit.fetch(topic).map { |limit| Window.new(self, limit) }
8
+ success!
12
9
  end
13
10
 
14
- def perform!
15
- validate_limit!
16
-
17
- yield if block_given?
11
+ def throttle_with_block!
12
+ if reloaded_limit_exceeded?
13
+ failure!
14
+ raise Errors::LimitExceededError, exceeded_window
15
+ end
18
16
 
19
- increment_cache_counter
17
+ yield
20
18
 
21
- true
19
+ success!
22
20
  end
23
21
 
24
- def perform_only_failures!
25
- validate_limit!
22
+ def throttle_only_failures_with_block!
23
+ return failure! if reloaded_limit_exceeded?
26
24
 
27
25
  begin
28
- yield if block_given?
26
+ yield
29
27
  rescue StandardError => e
30
- increment_cache_counter
28
+ success!
31
29
  raise e
32
30
  end
33
-
34
- true
35
- end
36
-
37
- def limit_exceeded?
38
- Window.find_exceeded(windows).present?
39
- end
40
-
41
- def increment_cache_counter
42
- Window.increment_cache_counter(windows)
43
- end
44
-
45
- def clear_cache_counter
46
- Window.clear_cache_counter(windows)
47
- end
48
-
49
- private
50
-
51
- def validate_limit!
52
- exceeded_window = Window.find_exceeded(windows)
53
-
54
- raise Errors::LimitExceededError, exceeded_window if exceeded_window
55
31
  end
56
32
  end
57
33
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RateLimit
4
- VERSION = '0.0.1'
4
+ VERSION = '0.1.0'
5
5
  end
@@ -2,14 +2,14 @@
2
2
 
3
3
  module RateLimit
4
4
  class Window
5
- attr_accessor :throttler, :limit
5
+ attr_accessor :worker, :limit
6
6
 
7
- delegate :topic, :namespace, :value, to: :throttler
7
+ delegate :topic, :namespace, :value, to: :worker
8
8
  delegate :threshold, :interval, to: :limit
9
9
 
10
- def initialize(throttler, limit)
11
- @throttler = throttler
12
- @limit = limit
10
+ def initialize(worker, limit)
11
+ @worker = worker
12
+ @limit = limit
13
13
  end
14
14
 
15
15
  def key
@@ -21,6 +21,10 @@ module RateLimit
21
21
  end
22
22
 
23
23
  class << self
24
+ def find_all(topic:, worker:)
25
+ Limit.fetch(topic).map { |limit| Window.new(worker, limit) }
26
+ end
27
+
24
28
  def find_exceeded(windows)
25
29
  windows.find { |w| w.cached_counter >= w.threshold }
26
30
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RateLimit
4
+ class Worker
5
+ include Throttler
6
+
7
+ attr_accessor :topic, :namespace, :value, :limits, :windows, :exceeded_window, :result
8
+
9
+ def initialize(topic:, value:, namespace: nil)
10
+ @topic = topic.to_s
11
+ @value = value.to_i
12
+ @namespace = namespace&.to_s
13
+ @windows = Window.find_all(worker: self, topic: @topic)
14
+ end
15
+
16
+ def increment_cache_counter
17
+ Window.increment_cache_counter(windows)
18
+ end
19
+
20
+ def clear_cache_counter
21
+ Window.clear_cache_counter(windows)
22
+ end
23
+
24
+ def reloaded_limit_exceeded?
25
+ @exceeded_window = Window.find_exceeded(windows)
26
+
27
+ limit_exceeded?
28
+ end
29
+
30
+ def limit_exceeded?
31
+ exceeded_window.present?
32
+ end
33
+
34
+ def success!
35
+ increment_cache_counter
36
+ @result = Result.new(self, true)
37
+ RateLimit.config.success_callback(result)
38
+ end
39
+
40
+ def failure!
41
+ @result = Result.new(self, false)
42
+ RateLimit.config.failure_callback(result)
43
+ end
44
+ end
45
+ end
data/lib/rate_limit.rb CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  require 'active_support/core_ext/module'
4
4
  require_relative 'rate_limit/configurable'
5
+ require_relative 'rate_limit/result'
5
6
  require_relative 'rate_limit/cache'
6
7
  require_relative 'rate_limit/window'
7
8
  require_relative 'rate_limit/throttler'
9
+ require_relative 'rate_limit/worker'
8
10
  require_relative 'rate_limit/limit'
9
11
  require_relative 'rate_limit/errors/limit_exceeded_error'
10
12
  require_relative 'rate_limit/base'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rate-limit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mohamed Motaweh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-09 00:00:00.000000000 Z
11
+ date: 2022-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -60,6 +60,7 @@ files:
60
60
  - CHANGELOG.md
61
61
  - LICENSE
62
62
  - README.md
63
+ - lib/rate-limit.rb
63
64
  - lib/rate_limit.rb
64
65
  - lib/rate_limit/base.rb
65
66
  - lib/rate_limit/cache.rb
@@ -69,9 +70,11 @@ files:
69
70
  - lib/rate_limit/configurable.rb
70
71
  - lib/rate_limit/errors/limit_exceeded_error.rb
71
72
  - lib/rate_limit/limit.rb
73
+ - lib/rate_limit/result.rb
72
74
  - lib/rate_limit/throttler.rb
73
75
  - lib/rate_limit/version.rb
74
76
  - lib/rate_limit/window.rb
77
+ - lib/rate_limit/worker.rb
75
78
  homepage: https://github.com/catawiki/rate-limit
76
79
  licenses:
77
80
  - MIT
@@ -95,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
98
  - !ruby/object:Gem::Version
96
99
  version: '0'
97
100
  requirements: []
98
- rubygems_version: 3.1.6
101
+ rubygems_version: 3.2.3
99
102
  signing_key:
100
103
  specification_version: 4
101
104
  summary: A Rate Limiting Gem