rate-limit 0.1.0 → 0.2.1

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: 4b73af03f6ea918dd965b1f1d89b8f7131e1fbba2b94166a397c100510c4a426
4
- data.tar.gz: fcd12f564f38c214a088032b9ee912337dac4a566a1cd97c39256180e2e8e8a5
3
+ metadata.gz: 11a836041207b190f37ebc39fc5f3dad8f550fee8193c4a1bb7a0ec1add638f1
4
+ data.tar.gz: a213f8f17a0630f7a8f4d43ec8c7072e4f0f1ecbc015723aa06aeb1ea802883d
5
5
  SHA512:
6
- metadata.gz: 2a9e190cda1984c96ffd6d93751e56a323d783778c170a492d0b67b151d4a85ef83c88907526f48dc0cc574bdb19880a09667f2472b4e5873d54262e8b7dd976
7
- data.tar.gz: b0529be97de2244366f3ac99f6192e7e649ff8574e15b44f51e2d56030dbcb9163238a1a03d77ed3b49d67c5669cba865c674216aa83dfe73877ab837aae02dd
6
+ metadata.gz: 77dd6eefc19f5af422efd9f1d31db21a0697c0325cf6d39bc02798112fef5706604c6ee15418df49ef75ee7f6801dcd576f6a8ebfaa306c01defaf7f378f8017
7
+ data.tar.gz: 5580d905fcbf812e925ea22a4b203043ee12d68996cbe4bb0589cbd74ecaf85a04cb2d6ea1a37cf3f23b5861e0bae76f921269de3fe24ce5dcfc405325b74bea
data/CHANGELOG.md CHANGED
@@ -7,14 +7,51 @@ and this project adheres to [Semantic Versioning].
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [v0.1.0] - 2022-09-16
10
+ ## [v0.2.1] - 2022-11-09
11
+
12
+ ### Added
13
+
14
+ - https://github.com/catawiki/rate-limit/pull/28 added Integration Tests
15
+
16
+ ### Fixed
17
+
18
+ - https://github.com/catawiki/rate-limit/issues/29 Calling throttle with only_failures: true does not call success callback
19
+
20
+
21
+ ## [v0.2.0] - 2022-10-05
22
+
23
+ ### Added
24
+
25
+ - added `RateLimit::Errors::LimitExceededError#result`
26
+ - added `Worker#raise_errors`
27
+ - added `Worker#only_failures`
28
+
29
+ ### Changed
30
+
31
+ - changed `RateLimit::Result.initialize`
32
+
33
+ ### Removed
34
+
35
+ - removed `RateLimit::Result#namespace`
36
+ - removed `RateLimit::Worker#namespace`
37
+ - removed `RateLimit::Errors::LimitExceededError#namespace`
38
+ - removed `RateLimit::Errors::LimitExceededError#worker`
39
+ - removed `RateLimit.throttle_with_block!` and `Worker#throttle_with_block!` in favour of `.throttle` with `{ raise_errors: true }` option
40
+ - removed `RateLimit.throttle_only_failures_with_block!` and `Worker#throttle_only_failures_with_block!` in favour of `.throttle` with `{ only_failures: true }` option
41
+
42
+
43
+ ### Fixed
44
+
45
+ - https://github.com/catawiki/rate-limit/issues/16 String Values are not respected
46
+
47
+ ## [v0.1.0] - 2022-09-19
11
48
 
12
49
 
13
50
  ### Added
14
51
 
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`
52
+ - https://github.com/catawiki/rate-limit/pull/11 `RateLimit::Result` class
53
+ - https://github.com/catawiki/rate-limit/pull/12 `RateLimit::Worker` class
54
+ - https://github.com/catawiki/rate-limit/pull/13 `RateLimit::Config#on_success` and `RateLimit::Config#on_failure`
18
55
 
19
56
  ### Changed
20
57
 
@@ -26,8 +63,8 @@ and this project adheres to [Semantic Versioning].
26
63
 
27
64
  ### Fixed
28
65
 
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
66
+ - https://github.com/catawiki/rate-limit/issues/7 Symbol topic names does not load the correct limits
67
+ - https://github.com/catawiki/rate-limit/issues/6 Main Module (RateLimit) fails to autoload
31
68
 
32
69
 
33
70
  ## v0.0.1 - 2022-09-09
@@ -39,5 +76,7 @@ and this project adheres to [Semantic Versioning].
39
76
 
40
77
  <!-- versions -->
41
78
 
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
79
+ [Unreleased]: https://github.com/catawiki/rate-limit/compare/v0.2.1...HEAD
80
+ [v0.2.1]: https://github.com/catawiki/rate-limit/compare/v0.2.0...v0.2.1
81
+ [v0.2.0]: https://github.com/catawiki/rate-limit/compare/v0.1.0...v0.2.0
82
+ [v0.1.0]: https://github.com/catawiki/rate-limit/compare/v0.0.1...v0.1.0
data/README.md CHANGED
@@ -3,12 +3,12 @@
3
3
 
4
4
  Protect your Ruby apps from bad actors. RateLimit allows you to set permissions as to whether certain number of feature calls are valid or not for a specific entity (user, phone number, email address, etc...).
5
5
 
6
- This gem mainly provides brute-force protection by throttling attepmts for a specific entity id (i.e user_id). However it could also be used to throttle based on ip address (we recommend that you consider using [Rack::Attack](https://github.com/rack/rack-attack) for more optimized ip throttling)
6
+ This gem mainly provides brute-force protection by throttling attempts for a specific entity id (i.e user_id). However it could also be used to throttle based on ip address (we recommend that you consider using [Rack::Attack](https://github.com/rack/rack-attack) for more optimized ip throttling)
7
7
 
8
8
  #### Common Use Cases
9
9
  * [Login] Brute-force attempts for a spefic account
10
- * [SMS Spam] Brute-force attempts for requesting Phone Verification SMS for a spefic user_id
11
- * [SMS Spam] Brute-force attempts for requesting Phone Verification SMS for a spefic phone_number
10
+ * [SMS Spam] Brute-force attempts for requesting Phone Verification SMS for a specific user_id
11
+ * [SMS Spam] Brute-force attempts for requesting Phone Verification SMS for a specific phone_number
12
12
  * [Verifications] Brute-force attempts for entering verification codes
13
13
  * [Redeem] Brute-force attempts to redeem voucher codes from a specific account
14
14
 
@@ -28,85 +28,32 @@ Or install it yourself as:
28
28
 
29
29
  $ gem install rate-limit
30
30
 
31
- ## Usage
31
+ ## Basic Usage
32
32
 
33
- #### Basic `RateLimit.throttle`
33
+ ### [`RateLimit.throttle`](https://github.com/catawiki/rate-limit/wiki/Throttling)
34
+ The throttle method expects the following options
34
35
 
35
- ```ruby
36
- result = RateLimit.throttle(topic: :login, namespace: :user_id, value: id)
36
+ | Option | Description | Examples |
37
+ | ---------------- | ------------------------------------------------------------------------------- | ------------------------------------- |
38
+ | topic | The topic you would like to throttle | "login", "send_sms", "redeem_voucher" |
39
+ | value | The identifier of the unique entity that is throttled based on the topic limits | user_id, phone_number, voucher_code |
37
40
 
38
- if result.success?
39
- # Do something
40
- end
41
- ```
42
- or
43
41
 
42
+ The `throttle` method checks if the given value did exceed the defined limits for the given topic. If the limit is exceeded then it returns [RateLimit::Result](https://github.com/catawiki/rate-limit/wiki/RateLimit::Result) Object, where `result.success?` will be `false`. Otherwise, it increments the attempts counter in the cache and sets `result.success?` to `true`.
43
+
44
+ #### Example
44
45
  ```ruby
45
- result = RateLimit.throttle(topic: :login, namespace: :user_id, value: id)
46
+ result = RateLimit.throttle(topic: :login, value: 123)
46
47
 
47
48
  if result.success?
48
49
  # Do something
49
50
  end
50
51
  ```
51
52
 
52
- #### Basic with exception `RateLimit.throttle!`
53
-
54
- ```ruby
55
- begin
56
- RateLimit.throttle_with_block!(topic: :send_sms, namespace: :user_id, value: id) do
57
- # Logic goes Here
58
- end
59
- rescue RateLimit::Errors::LimitExceededError => e
60
- # Error Handling Logic goes here
61
- e.topic # :login
62
- e.namespace # :user_id
63
- e.value # id
64
- e.threshold # 2
65
- e.interval # 60
66
- end
67
- ```
68
-
69
- #### Advanced
70
-
71
- ```ruby
72
- throttler = RateLimit::Worker.new(topic: :login, namespace: :user_id, value: id)
73
-
74
- begin
75
- throttler.throttle_with_block! do
76
- # Logic goes Here
77
- end
78
- rescue RateLimit::Errors::LimitExceededError => e
79
- # Error Handling Logic goes here
80
- end
81
- ```
82
-
83
- #### Manual
84
53
 
85
- ```ruby
86
- throttler = RateLimit::Worker.new(topic: :login, namespace: :user_id, value: id)
87
-
88
- unless throttler.limit_exceeded?
89
- # Logic goes Here
90
-
91
- throttler.increment_counters
92
- end
93
- ```
94
-
95
- #### Nested throttles
96
-
97
- ```ruby
98
- begin
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
101
- # Logic goes Here
102
- end
103
- end
104
- rescue RateLimit::Errors::LimitExceededError => e
105
- # Error Handling Logic goes here
106
- end
107
- ```
54
+ Please check the [Wiki](https://github.com/catawiki/rate-limit/wiki) for advanced throttling and options.
108
55
 
109
- ### Config
56
+ ## [Configuration](https://github.com/catawiki/rate-limit/wiki/Configuration)
110
57
 
111
58
  Customize the configuration by adding the following block to `config/initializers/rate_limit.rb`
112
59
 
@@ -119,11 +66,11 @@ RateLimit.configure do |config|
119
66
  config.limits_file_path = 'config/rate-limit.yml'
120
67
  config.on_success = proc { |result|
121
68
  # Success Logic Goes HERE
122
- # result.topic, result.namespace, result.value
69
+ # result.topic, result.value
123
70
  }
124
71
  config.on_failure = proc { |result|
125
72
  # Failure Logic Goes HERE
126
- # result.topic, result.namespace, result.value, result.threshold, result.interval
73
+ # result.topic, result.value, result.threshold, result.interval
127
74
  }
128
75
  end
129
76
  ```
@@ -4,21 +4,7 @@ module RateLimit
4
4
  module Base
5
5
  def throttle(**args)
6
6
  worker = Worker.new(**args)
7
- worker.throttle
8
- worker.result
9
- end
10
-
11
- def throttle_with_block!(**args, &block)
12
- worker = Worker.new(**args)
13
-
14
- worker.throttle_with_block!(&block)
15
- worker.result
16
- end
17
-
18
- def throttle_only_failures_with_block!(**args, &block)
19
- worker = Worker.new(**args)
20
-
21
- worker.throttle_only_failures_with_block!(&block)
7
+ worker.throttle { yield if block_given? }
22
8
  worker.result
23
9
  end
24
10
 
@@ -3,18 +3,18 @@
3
3
  module RateLimit
4
4
  module Errors
5
5
  class LimitExceededError < StandardError
6
- attr_reader :window
6
+ attr_reader :result
7
7
 
8
- delegate :topic, :namespace, :value, :threshold, :interval, to: :window
8
+ delegate :topic, :value, :threshold, :interval, to: :result
9
9
 
10
- def initialize(window)
11
- @window = window
10
+ def initialize(result)
11
+ @result = result
12
12
 
13
13
  super(custom_message)
14
14
  end
15
15
 
16
16
  def custom_message
17
- "#{topic}: #{namespace} has exceeded #{threshold} in #{interval} seconds"
17
+ "#{result.topic}: has exceeded #{result.threshold} in #{result.interval} seconds"
18
18
  end
19
19
  end
20
20
  end
@@ -3,20 +3,26 @@
3
3
  module RateLimit
4
4
  class Result
5
5
  # Attributes
6
- attr_accessor :topic, :namespace, :value, :threshold, :interval
6
+ attr_accessor :topic, :value, :threshold, :interval
7
7
 
8
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
9
+ def initialize(topic:, value:)
10
+ @topic = topic
11
+ @value = value
16
12
  end
17
13
 
18
14
  def success?
19
15
  @success
20
16
  end
17
+
18
+ def success!
19
+ @success = true
20
+ end
21
+
22
+ def failure!(worker)
23
+ @success = false
24
+ @threshold = worker.exceeded_window&.threshold
25
+ @interval = worker.exceeded_window&.interval
26
+ end
21
27
  end
22
28
  end
@@ -5,29 +5,12 @@ module RateLimit
5
5
  def throttle
6
6
  return failure! if reloaded_limit_exceeded?
7
7
 
8
- success!
9
- end
10
-
11
- def throttle_with_block!
12
- if reloaded_limit_exceeded?
13
- failure!
14
- raise Errors::LimitExceededError, exceeded_window
15
- end
16
-
17
- yield
18
-
19
- success!
20
- end
21
-
22
- def throttle_only_failures_with_block!
23
- return failure! if reloaded_limit_exceeded?
8
+ yield if block_given?
24
9
 
25
- begin
26
- yield
27
- rescue StandardError => e
28
- success!
29
- raise e
30
- end
10
+ success!(skip_increment_cache: only_failures)
11
+ rescue StandardError => e
12
+ success! unless e.is_a?(Errors::LimitExceededError)
13
+ raise e
31
14
  end
32
15
  end
33
16
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RateLimit
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.1'
5
5
  end
@@ -4,7 +4,7 @@ module RateLimit
4
4
  class Window
5
5
  attr_accessor :worker, :limit
6
6
 
7
- delegate :topic, :namespace, :value, to: :worker
7
+ delegate :topic, :value, to: :worker
8
8
  delegate :threshold, :interval, to: :limit
9
9
 
10
10
  def initialize(worker, limit)
@@ -13,7 +13,7 @@ module RateLimit
13
13
  end
14
14
 
15
15
  def key
16
- @key ||= [topic, namespace, value, interval].join(':')
16
+ @key ||= [topic, value, interval].join(':')
17
17
  end
18
18
 
19
19
  def cached_counter
@@ -4,13 +4,15 @@ module RateLimit
4
4
  class Worker
5
5
  include Throttler
6
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)
7
+ attr_accessor :topic, :value, :limits, :windows, :exceeded_window, :result, :raise_errors, :only_failures
8
+
9
+ def initialize(topic:, value:, raise_errors: false, only_failures: false)
10
+ @topic = topic.to_s
11
+ @value = value.to_s
12
+ @windows = Window.find_all(worker: self, topic: @topic)
13
+ @result = Result.new(topic: @topic, value: @value)
14
+ @raise_errors = raise_errors
15
+ @only_failures = only_failures
14
16
  end
15
17
 
16
18
  def increment_cache_counter
@@ -31,15 +33,17 @@ module RateLimit
31
33
  exceeded_window.present?
32
34
  end
33
35
 
34
- def success!
35
- increment_cache_counter
36
- @result = Result.new(self, true)
36
+ def success!(skip_increment_cache: false)
37
+ increment_cache_counter unless skip_increment_cache
38
+ result.success!
37
39
  RateLimit.config.success_callback(result)
38
40
  end
39
41
 
40
42
  def failure!
41
- @result = Result.new(self, false)
43
+ result.failure!(self)
42
44
  RateLimit.config.failure_callback(result)
45
+
46
+ raise Errors::LimitExceededError, result if raise_errors
43
47
  end
44
48
  end
45
49
  end
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.1.0
4
+ version: 0.2.1
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-19 00:00:00.000000000 Z
11
+ date: 2022-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport