better_rate_limit 0.1.2 → 0.1.6

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: b8202a0675862d02c311bc2c05f294d5adf1a1add2e5cf65aa401edcb0c159d5
4
- data.tar.gz: b94eb54339bdd7347dbc83f4ac95f87f413671227c89e072bbc0d56bc5a5fb65
3
+ metadata.gz: fd081be677a01d7f69d7e6e6ada8a3172d84d6ee74fbf7ccab5d5fbecb89ee58
4
+ data.tar.gz: 2817642bfee1165564273b8b36ae434e02e7135c6e63a887a4c178c4558d3752
5
5
  SHA512:
6
- metadata.gz: '07802df810af301f0d5d1fd8f1bd4f924b535e1dddc98575faa20ec20093d88777f27dc9f99cd6d17d7dd9163ea5760300af5b266ae5a5e120cb03c31c0ee2ea'
7
- data.tar.gz: 2659222d45eb6a1d2a6559ef78c8a64fefd7e0c66a06d38b96810782063059037d6f8da8a6453b55aa8adc9c7d6e70b7c98797405c9b4258b908ed339c788d89
6
+ metadata.gz: bdd3fda987a39c8027d47ddec7b6112e089a609aaabd3a924e71a76417619b69284416183d68744ea05fa55b47e1cead0d04cb99ddb33937e6db97f578d784db
7
+ data.tar.gz: 7b03b579af9be74f8669af5531d254580b0f384232dc5cb295f6d4cbcea7ca3525d42ee41105f0c08dd121c6a68d91544e06dd38a7044bd0b4062cc6007077c5
data/.gitignore CHANGED
@@ -1,61 +1,3 @@
1
- <<<<<<< HEAD
2
- *.gem
3
- *.rbc
4
- /.config
5
- /coverage/
6
- /InstalledFiles
7
- /pkg/
8
- /spec/reports/
9
- /spec/examples.txt
10
- /test/tmp/
11
- /test/version_tmp/
12
- /tmp/
13
-
14
- # Used by dotenv library to load environment variables.
15
- # .env
16
-
17
- # Ignore Byebug command history file.
18
- .byebug_history
19
-
20
- ## Specific to RubyMotion:
21
- .dat*
22
- .repl_history
23
- build/
24
- *.bridgesupport
25
- build-iPhoneOS/
26
- build-iPhoneSimulator/
27
-
28
- ## Specific to RubyMotion (use of CocoaPods):
29
- #
30
- # We recommend against adding the Pods directory to your .gitignore. However
31
- # you should judge for yourself, the pros and cons are mentioned at:
32
- # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
33
- #
34
- # vendor/Pods/
35
-
36
- ## Documentation cache and generated files:
37
- /.yardoc/
38
- /_yardoc/
39
- /doc/
40
- /rdoc/
41
-
42
- ## Environment normalization:
43
- /.bundle/
44
- /vendor/bundle
45
- /lib/bundler/man/
46
-
47
- # for a library or gem, you might want to ignore these files since the code is
48
- # intended to run in multiple environments; otherwise, check them in:
49
- # Gemfile.lock
50
- # .ruby-version
51
- # .ruby-gemset
52
-
53
- # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
54
- .rvmrc
55
-
56
- # Used by RuboCop. Remote config files pulled in from inherit_from directive.
57
- # .rubocop-https?--*
58
- =======
59
1
  /.bundle/
60
2
  /.yardoc
61
3
  /_yardoc/
@@ -64,7 +6,5 @@ build-iPhoneSimulator/
64
6
  /pkg/
65
7
  /spec/reports/
66
8
  /tmp/
67
-
68
- # rspec failure tracking
69
- .rspec_status
70
- >>>>>>> init gem
9
+ *.gem
10
+ .byebug_history
data/.travis.yml CHANGED
@@ -2,5 +2,5 @@
2
2
  language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
- - 2.6.5
5
+ - 2.6.6
6
6
  before_install: gem install bundler -v 2.1.4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- better_rate_limit (0.1.2)
4
+ better_rate_limit (0.1.5)
5
5
  actionpack (>= 5.0)
6
6
  redis (>= 3.3)
7
7
 
@@ -59,7 +59,7 @@ GEM
59
59
  rails-html-sanitizer (1.3.0)
60
60
  loofah (~> 2.3)
61
61
  rake (12.3.3)
62
- redis (4.2.1)
62
+ redis (4.2.5)
63
63
  thread_safe (0.3.6)
64
64
  timecop (0.9.1)
65
65
  tzinfo (1.2.7)
data/README.md CHANGED
@@ -1,4 +1,6 @@
1
1
  # BetterRateLimit
2
+ [![Build Status](https://travis-ci.org/upscopeio/better_rate_limit.svg?branch=master)](https://travis-ci.org/upscopeio/better_rate_limit)
3
+ [![Gem Version](https://badge.fury.io/rb/better_rate_limit.svg)](https://badge.fury.io/rb/better_rate_limit)
2
4
 
3
5
  Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/better_rate_limit`. To experiment with that code, run `bin/console` for an interactive prompt.
4
6
 
@@ -7,7 +9,7 @@ Welcome to your new gem! In this directory, you'll find the files you need to be
7
9
  Add this line to your application's Gemfile:
8
10
 
9
11
  ```ruby
10
- gem 'better_rate_limit', git: 'https://github.com/upscopeio/better_rate_limit'
12
+ gem 'better_rate_limit'
11
13
  ```
12
14
 
13
15
  And then execute:
@@ -39,6 +41,15 @@ ActiveSupport::Notifications.subscribe /rate_limit/ do |_name, _start, _finish,
39
41
  end
40
42
  ```
41
43
 
44
+ ### Configuration
45
+ You can ignore the throttle by using the following configuration
46
+ ```ruby
47
+ BetterRateLimit.configure do |config|
48
+ config.ignore = true
49
+ end
50
+ ```
51
+ Useful to ignore the throttle on tests.
52
+
42
53
  ## Development
43
54
 
44
55
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,23 +1,26 @@
1
1
  require 'ostruct'
2
2
  require 'better_rate_limit/throttle'
3
+ require 'better_rate_limit/limit'
3
4
 
4
5
  module ActionController
5
6
  module BetterRateLimit
6
7
  extend ActiveSupport::Concern
7
8
 
8
9
  module ClassMethods
9
- def rate_limit(max, every:, name: nil, scope: -> { real_ip }, only: [], except: [])
10
- rate_limits << OpenStruct.new({
11
- max: max,
12
- every: every,
13
- name: name || controller_path,
14
- scope: scope,
15
- only: only,
16
- except: except,
17
- controller_path: controller_path
10
+ def rate_limit(max, options)
11
+ rate_limits << Limit.build(max, controller_path, {
12
+ if: options[:if],
13
+ unless: options[:unless],
14
+ every: options[:every],
15
+ name: options[:name] || controller_path,
16
+ scope: options[:scope] || -> { real_ip },
17
+ only: options[:only] || [],
18
+ except: options[:except] || [],
19
+ clear_if: options[:clear_if]
18
20
  })
19
21
 
20
22
  before_action :perform_rate_limiting
23
+ after_action :clear_keys
21
24
  end
22
25
 
23
26
  def rate_limits
@@ -41,6 +44,20 @@ module ActionController
41
44
  render file: 'public/429.html', status: :too_many_requests, layout: false
42
45
  end
43
46
 
47
+ def clear_keys
48
+ rate_limits = self.class.all_rate_limits.filter(&:clear_if_present?)
49
+ return if rate_limits.empty?
50
+
51
+ rate_limits.each do |rate_limit|
52
+ should_clear = rate_limit.clear_if.is_a?(Proc) ? instance_exec(&rate_limit.clear_if) : send(rate_limit.clear_if)
53
+ next unless should_clear
54
+
55
+ scope = rate_limit.scope.is_a?(Proc) ? instance_exec(&rate_limit.scope) : send(rate_limit.scope)
56
+ scope = scope.to_param if scope.respond_to?(:to_param)
57
+ ::BetterRateLimit::Throttle.clear(rate_limit.key(scope))
58
+ end
59
+ end
60
+
44
61
  private
45
62
 
46
63
  def json?
@@ -52,7 +69,17 @@ module ActionController
52
69
  end
53
70
 
54
71
  def under_rate_limit?(limit)
55
- if limit.controller_path == controller_path
72
+ if limit.has_if_condition?
73
+ if_condition = limit._if.is_a?(Proc) ? instance_exec(&limit._if) : send(limit._if)
74
+ return true unless if_condition
75
+ end
76
+
77
+ if limit.has_unless_condition?
78
+ unless_condition = limit._unless.is_a?(Proc) ? instance_exec(&limit._unless) : send(limit._unless)
79
+ return true if unless_condition
80
+ end
81
+
82
+ if limit.controller_path_is?(controller_path)
56
83
  return true if action_name.to_sym.in?([limit.except].flatten)
57
84
  return true if !limit.only.empty? && !action_name.to_sym.in?([limit.only].flatten)
58
85
  end
@@ -60,9 +87,7 @@ module ActionController
60
87
  scope = limit.scope.is_a?(Proc) ? instance_exec(&limit.scope) : send(limit.scope)
61
88
  scope = scope.to_param if scope.respond_to?(:to_param)
62
89
 
63
- key = ['controller_throttle', limit.name, limit.max, limit.every, scope].join(':')
64
-
65
- ::BetterRateLimit::Throttle.allow? key, limit: limit.max, time_window: limit.every
90
+ ::BetterRateLimit::Throttle.allow? limit.key(scope), limit: limit.max, time_window: limit.every
66
91
  end
67
92
  end
68
93
  end
@@ -1,4 +1,21 @@
1
1
  require 'action_controller'
2
+ require 'better_rate_limit/configuration'
3
+
4
+ module BetterRateLimit
5
+ class << self
6
+ def configuration
7
+ @configuration ||= Configuration.new
8
+ end
9
+
10
+ def configure
11
+ yield configuration
12
+ end
13
+
14
+ def reset_configuration
15
+ @configuration = Configuration.new
16
+ end
17
+ end
18
+ end
2
19
 
3
20
  module ActionController
4
21
  autoload :BetterRateLimit, 'action_controller/better_rate_limit'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterRateLimit
4
+ class Configuration
5
+ attr_accessor :ignore
6
+
7
+ def initialize
8
+ @ignore = false
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,75 @@
1
+ class Limit
2
+ def self.build(max, controller_path, options)
3
+ options.assert_valid_keys(:if, :unless, :every, :name, :scope, :only, :except, :clear_if)
4
+ new(max, controller_path, {
5
+ if: options[:if],
6
+ unless: options[:unless],
7
+ every: options[:every],
8
+ name: options[:name],
9
+ scope: options[:scope],
10
+ only: options[:only],
11
+ except: options[:except],
12
+ clear_if: options[:clear_if]
13
+ })
14
+ end
15
+
16
+ attr_reader :max, :controller_path
17
+
18
+ def initialize(max, controller_path, options)
19
+ @max = max
20
+ @controller_path = controller_path
21
+ @options = options
22
+ end
23
+
24
+ def _if
25
+ @options[:if]
26
+ end
27
+
28
+ def name
29
+ @options[:name]
30
+ end
31
+
32
+ def every
33
+ @options[:every]
34
+ end
35
+
36
+ def _unless
37
+ @options[:unless]
38
+ end
39
+
40
+ def except
41
+ @options[:except]
42
+ end
43
+
44
+ def only
45
+ @options[:only]
46
+ end
47
+
48
+ def scope
49
+ @options[:scope]
50
+ end
51
+
52
+ def clear_if
53
+ @options[:clear_if]
54
+ end
55
+
56
+ def clear_if_present?
57
+ @options[:clear_if].present?
58
+ end
59
+
60
+ def has_if_condition?
61
+ _if.present?
62
+ end
63
+
64
+ def has_unless_condition?
65
+ _unless.present?
66
+ end
67
+
68
+ def controller_path_is?(controller_path)
69
+ self.controller_path == controller_path
70
+ end
71
+
72
+ def key(key_scope)
73
+ ['controller_throttle', name, max, every, key_scope].join(':')
74
+ end
75
+ end
@@ -10,6 +10,8 @@ module BetterRateLimit
10
10
 
11
11
  class << self
12
12
  def throttle(key, limit:, time_window:)
13
+ return true if BetterRateLimit.configuration.ignore
14
+
13
15
  now = Time.now.utc
14
16
  timestamps_count = redis_client.llen key
15
17
 
@@ -21,11 +23,14 @@ module BetterRateLimit
21
23
  true
22
24
  else
23
25
  first = redis_client.lpop(key)
26
+
24
27
  redis_client.multi do
25
28
  redis_client.rpush key, now
26
29
  redis_client.expire key, time_window.to_i
27
30
  end
28
31
 
32
+ return false unless first
33
+
29
34
  passing = first.to_time(:utc) < time_window.ago
30
35
 
31
36
  unless passing
@@ -37,6 +42,11 @@ module BetterRateLimit
37
42
  end
38
43
  end
39
44
 
45
+ def clear(key)
46
+ redis_client.del(key)
47
+ redis_client.del("failing-rate-limits:#{key}")
48
+ end
49
+
40
50
  alias allow? throttle
41
51
 
42
52
  def notify(key)
@@ -1,3 +1,3 @@
1
1
  module BetterRateLimit
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.6"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: better_rate_limit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pablo Fonseca
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-08-10 00:00:00.000000000 Z
12
+ date: 2021-07-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
@@ -58,6 +58,8 @@ files:
58
58
  - bin/setup
59
59
  - lib/action_controller/better_rate_limit.rb
60
60
  - lib/better_rate_limit.rb
61
+ - lib/better_rate_limit/configuration.rb
62
+ - lib/better_rate_limit/limit.rb
61
63
  - lib/better_rate_limit/redis_connection.rb
62
64
  - lib/better_rate_limit/throttle.rb
63
65
  - lib/better_rate_limit/version.rb