rate-limit 0.0.1 → 0.1.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: 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