throttling 0.3.1 → 0.4.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 +7 -0
- checksums.yaml.gz.sig +4 -0
- data/CHANGELOG.md +20 -8
- data/Gemfile +1 -1
- data/Gemfile.lock +88 -0
- data/README.md +136 -109
- data/Rakefile +3 -3
- data/certs/kpumuk.pem +21 -0
- data/lib/throttling/base.rb +6 -6
- data/lib/throttling/indifferent_access.rb +31 -22
- data/lib/throttling/version.rb +1 -1
- data/lib/throttling.rb +14 -14
- data/throttling.gemspec +35 -20
- data.tar.gz.sig +2 -0
- metadata +82 -64
- metadata.gz.sig +0 -0
- data/.gitignore +0 -18
- data/Guardfile +0 -6
- data/spec/base_spec.rb +0 -181
- data/spec/fixtures/throttling.yml +0 -28
- data/spec/spec_helper.rb +0 -21
- data/spec/throttling_spec.rb +0 -103
data/lib/throttling.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "logger"
|
2
|
+
require "yaml"
|
3
3
|
|
4
4
|
# Simple throttling library to limit number of actions in time.
|
5
5
|
module Throttling
|
@@ -8,7 +8,7 @@ module Throttling
|
|
8
8
|
# (if it is a Rails application).
|
9
9
|
def storage
|
10
10
|
@@storage ||= (defined?(Rails) && Rails.respond_to?(:cache) && Rails.cache) || nil
|
11
|
-
raise ArgumentError,
|
11
|
+
raise ArgumentError, "Throttling.storage is not specified" unless @@storage
|
12
12
|
@@storage
|
13
13
|
end
|
14
14
|
|
@@ -25,7 +25,7 @@ module Throttling
|
|
25
25
|
|
26
26
|
# Gets the logger used to output errors or warnings.
|
27
27
|
def logger
|
28
|
-
@@logger ||= (defined?(Rails) && Rails.respond_to(:logger) && Rails.logger) || Logger.new(
|
28
|
+
@@logger ||= (defined?(Rails) && Rails.respond_to(:logger) && Rails.logger) || Logger.new($stdout)
|
29
29
|
end
|
30
30
|
|
31
31
|
# Sets the logger used to output errors or warnings.
|
@@ -52,14 +52,14 @@ module Throttling
|
|
52
52
|
|
53
53
|
# Sets current throttling limits.
|
54
54
|
def limits=(limits)
|
55
|
-
@@limits = limits
|
55
|
+
@@limits = limits&.with_indifferent_access
|
56
56
|
end
|
57
57
|
|
58
58
|
# Get the value indicating whether throttling is enabled.
|
59
59
|
def enabled?
|
60
60
|
!!@@enabled
|
61
61
|
end
|
62
|
-
|
62
|
+
alias_method :enabled, :enabled?
|
63
63
|
|
64
64
|
# Sets the value indicating whether throttling is enabled.
|
65
65
|
def enabled=(enabled)
|
@@ -83,20 +83,20 @@ module Throttling
|
|
83
83
|
|
84
84
|
# Resets all values to their default state (mostly for testing purpose).
|
85
85
|
def reset_defaults!
|
86
|
-
@@enabled
|
87
|
-
@@logger
|
88
|
-
@@storage
|
86
|
+
@@enabled = true
|
87
|
+
@@logger = nil
|
88
|
+
@@storage = nil
|
89
89
|
@@limits_config = nil
|
90
90
|
|
91
91
|
# Internal variables
|
92
|
-
@@instances
|
93
|
-
@@config
|
92
|
+
@@instances = {}
|
93
|
+
@@config = nil
|
94
94
|
end
|
95
95
|
|
96
96
|
private
|
97
97
|
|
98
98
|
def load_config(path)
|
99
|
-
return nil unless File.
|
99
|
+
return nil unless File.exist?(path)
|
100
100
|
YAML.load_file(path).with_indifferent_access
|
101
101
|
end
|
102
102
|
end
|
@@ -104,6 +104,6 @@ module Throttling
|
|
104
104
|
reset_defaults!
|
105
105
|
end
|
106
106
|
|
107
|
-
require
|
108
|
-
require
|
107
|
+
require "throttling/indifferent_access"
|
108
|
+
require "throttling/base"
|
109
109
|
require "throttling/version"
|
data/throttling.gemspec
CHANGED
@@ -1,24 +1,39 @@
|
|
1
|
-
|
2
|
-
require File.expand_path('../lib/throttling/version', __FILE__)
|
1
|
+
require File.expand_path("../lib/throttling/version", __FILE__)
|
3
2
|
|
4
|
-
Gem::Specification.new do |
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
gem.homepage = "https://github.com/kpumuk/throttling"
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "throttling"
|
5
|
+
spec.version = Throttling::VERSION
|
6
|
+
spec.authors = ["Dmytro Shteflyuk", "Oleksiy Kovyrin"]
|
7
|
+
spec.email = ["kpumuk@kpumuk.info"]
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
spec.summary = "Easy throttling for Ruby applications"
|
10
|
+
spec.description = "Throttling gem provides basic, but very powerful way to throttle various user actions in your application"
|
11
|
+
spec.homepage = "https://github.com/kpumuk/throttling"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.platform = Gem::Platform::RUBY
|
14
|
+
spec.required_ruby_version = ">= 2.7.0"
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(\.|(bin|test|spec|features)/)}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
22
|
+
spec.add_development_dependency "rspec", "~> 3.13.0"
|
23
|
+
spec.add_development_dependency "timecop", "~> 0.9.6"
|
24
|
+
spec.add_development_dependency "standard", "~> 1.51.0"
|
25
|
+
|
26
|
+
spec.add_runtime_dependency "logger"
|
27
|
+
|
28
|
+
spec.cert_chain = ["certs/kpumuk.pem"]
|
29
|
+
spec.signing_key = File.expand_path("~/.gem/gem-private_key.pem") if $PROGRAM_NAME.end_with?("gem")
|
30
|
+
|
31
|
+
spec.metadata = {
|
32
|
+
"bug_tracker_uri" => "https://github.com/kpumuk/throttling/issues/",
|
33
|
+
"changelog_uri" => "https://github.com/kpumuk/throttling/blob/main/CHANGELOG.md",
|
34
|
+
"documentation_uri" => "https://rubydoc.info/github/kpumuk/throttling/",
|
35
|
+
"homepage_uri" => "https://github.com/kpumuk/throttling/",
|
36
|
+
"source_code_uri" => "https://github.com/kpumuk/throttling/",
|
37
|
+
"rubygems_mfa_required" => "true"
|
38
|
+
}
|
24
39
|
end
|
data.tar.gz.sig
ADDED
metadata
CHANGED
@@ -1,83 +1,107 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: throttling
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.4.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Dmytro Shteflyuk
|
9
8
|
- Oleksiy Kovyrin
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
bindir: exe
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQsFADA/MQ8wDQYDVQQDDAZrcHVt
|
14
|
+
dWsxFjAUBgoJkiaJk/IsZAEZFgZrcHVtdWsxFDASBgoJkiaJk/IsZAEZFgRpbmZv
|
15
|
+
MB4XDTI1MDkyOTE1MDM1M1oXDTI2MDkyOTE1MDM1M1owPzEPMA0GA1UEAwwGa3B1
|
16
|
+
bXVrMRYwFAYKCZImiZPyLGQBGRYGa3B1bXVrMRQwEgYKCZImiZPyLGQBGRYEaW5m
|
17
|
+
bzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALw2YroZc+IT+rs8NuPu
|
18
|
+
c13DelrxrpAgPEu1zuRb3l7WaHRNWA4TyS8Z6Aa1G2O+FdUZNMW1n7IwP/QMJ9Mz
|
19
|
+
ahRBiTmhik5kasJ9s0h1lq5/hZiycm0o5OtGioUzCkvk+UEMpzMHbLmVSZCzYciy
|
20
|
+
NDRDbXB0rLLu1eJk+gKgn6Qf5vj93h1w28BdWdaA7YegtbmipZ+pjmzCQAfPActT
|
21
|
+
6uXHG4dSo7Lz9jiFRI5dUizFbGXcRqkQ2b5AB8FFmfcvbqERvzQKBICnybjsKP0N
|
22
|
+
pJ3vGgO2sh5GvJFOPk1Vlur2nX9ZFznPEP1CEAQ+NFrmKRt355Z5sgOkAojSGnIL
|
23
|
+
/1sCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFPa4
|
24
|
+
VFc1YOlV1u/7EGTwMCAk8YE9MB0GA1UdEQQWMBSBEmtwdW11a0BrcHVtdWsuaW5m
|
25
|
+
bzAdBgNVHRIEFjAUgRJrcHVtdWtAa3B1bXVrLmluZm8wDQYJKoZIhvcNAQELBQAD
|
26
|
+
ggEBAFz/6+efHFEomTCA8V/eMpzaMkbj04cTmFxm28KiUO1F4vgbSIBx0bsMoJkH
|
27
|
+
KI956sPVVJMykwOdDUhRWBrHOVTBCGdkYWdb38KQKfwtkdGd8b/Afrxs5GuPME9r
|
28
|
+
TdfDc1aXCAPdLCajhpmuzfa1g8/W7ORdxwNQidYSSPJ5OvcAGSfxTHJsFseHjrBK
|
29
|
+
9hdoXZgqc4a5defxoOZFD9Im9AUwFqwR8njCBST6FbCGmHIYNHj2hxbwrU/1I2Kg
|
30
|
+
pPzkBOwQl2p1ykAEUe7cwoqJcO+1rPeTHjudNqhOfc3VnMA8A7uXOkTtYNVhvApn
|
31
|
+
oW4hE/8SnZkQm8jzdi0iX9nWxo8=
|
32
|
+
-----END CERTIFICATE-----
|
33
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
14
34
|
dependencies:
|
15
35
|
- !ruby/object:Gem::Dependency
|
16
36
|
name: rake
|
17
|
-
requirement:
|
18
|
-
none: false
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
19
38
|
requirements:
|
20
|
-
- -
|
39
|
+
- - "~>"
|
21
40
|
- !ruby/object:Gem::Version
|
22
|
-
version: '0'
|
41
|
+
version: '13.0'
|
23
42
|
type: :development
|
24
43
|
prerelease: false
|
25
|
-
version_requirements:
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '13.0'
|
26
49
|
- !ruby/object:Gem::Dependency
|
27
50
|
name: rspec
|
28
|
-
requirement:
|
29
|
-
none: false
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
30
52
|
requirements:
|
31
|
-
- -
|
53
|
+
- - "~>"
|
32
54
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
55
|
+
version: 3.13.0
|
34
56
|
type: :development
|
35
57
|
prerelease: false
|
36
|
-
version_requirements:
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 3.13.0
|
37
63
|
- !ruby/object:Gem::Dependency
|
38
64
|
name: timecop
|
39
|
-
requirement:
|
40
|
-
none: false
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
41
66
|
requirements:
|
42
|
-
- -
|
67
|
+
- - "~>"
|
43
68
|
- !ruby/object:Gem::Version
|
44
|
-
version:
|
69
|
+
version: 0.9.6
|
45
70
|
type: :development
|
46
71
|
prerelease: false
|
47
|
-
version_requirements:
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.9.6
|
48
77
|
- !ruby/object:Gem::Dependency
|
49
|
-
name:
|
50
|
-
requirement:
|
51
|
-
none: false
|
78
|
+
name: standard
|
79
|
+
requirement: !ruby/object:Gem::Requirement
|
52
80
|
requirements:
|
53
|
-
- -
|
81
|
+
- - "~>"
|
54
82
|
- !ruby/object:Gem::Version
|
55
|
-
version:
|
83
|
+
version: 1.51.0
|
56
84
|
type: :development
|
57
85
|
prerelease: false
|
58
|
-
version_requirements:
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 1.51.0
|
59
91
|
- !ruby/object:Gem::Dependency
|
60
|
-
name:
|
61
|
-
requirement:
|
62
|
-
none: false
|
92
|
+
name: logger
|
93
|
+
requirement: !ruby/object:Gem::Requirement
|
63
94
|
requirements:
|
64
|
-
- -
|
95
|
+
- - ">="
|
65
96
|
- !ruby/object:Gem::Version
|
66
97
|
version: '0'
|
67
|
-
type: :
|
98
|
+
type: :runtime
|
68
99
|
prerelease: false
|
69
|
-
version_requirements:
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: growl
|
72
|
-
requirement: &70097466941980 !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
100
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
101
|
requirements:
|
75
|
-
- -
|
102
|
+
- - ">="
|
76
103
|
- !ruby/object:Gem::Version
|
77
104
|
version: '0'
|
78
|
-
type: :development
|
79
|
-
prerelease: false
|
80
|
-
version_requirements: *70097466941980
|
81
105
|
description: Throttling gem provides basic, but very powerful way to throttle various
|
82
106
|
user actions in your application
|
83
107
|
email:
|
@@ -86,49 +110,43 @@ executables: []
|
|
86
110
|
extensions: []
|
87
111
|
extra_rdoc_files: []
|
88
112
|
files:
|
89
|
-
- .gitignore
|
90
113
|
- CHANGELOG.md
|
91
114
|
- Gemfile
|
92
|
-
-
|
115
|
+
- Gemfile.lock
|
93
116
|
- LICENSE
|
94
117
|
- README.md
|
95
118
|
- Rakefile
|
119
|
+
- certs/kpumuk.pem
|
96
120
|
- lib/throttling.rb
|
97
121
|
- lib/throttling/base.rb
|
98
122
|
- lib/throttling/indifferent_access.rb
|
99
123
|
- lib/throttling/version.rb
|
100
|
-
- spec/base_spec.rb
|
101
|
-
- spec/fixtures/throttling.yml
|
102
|
-
- spec/spec_helper.rb
|
103
|
-
- spec/throttling_spec.rb
|
104
124
|
- throttling.gemspec
|
105
125
|
homepage: https://github.com/kpumuk/throttling
|
106
|
-
licenses:
|
107
|
-
|
126
|
+
licenses:
|
127
|
+
- MIT
|
128
|
+
metadata:
|
129
|
+
bug_tracker_uri: https://github.com/kpumuk/throttling/issues/
|
130
|
+
changelog_uri: https://github.com/kpumuk/throttling/blob/main/CHANGELOG.md
|
131
|
+
documentation_uri: https://rubydoc.info/github/kpumuk/throttling/
|
132
|
+
homepage_uri: https://github.com/kpumuk/throttling/
|
133
|
+
source_code_uri: https://github.com/kpumuk/throttling/
|
134
|
+
rubygems_mfa_required: 'true'
|
108
135
|
rdoc_options: []
|
109
136
|
require_paths:
|
110
137
|
- lib
|
111
138
|
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
-
none: false
|
113
139
|
requirements:
|
114
|
-
- -
|
140
|
+
- - ">="
|
115
141
|
- !ruby/object:Gem::Version
|
116
|
-
version:
|
142
|
+
version: 2.7.0
|
117
143
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
-
none: false
|
119
144
|
requirements:
|
120
|
-
- -
|
145
|
+
- - ">="
|
121
146
|
- !ruby/object:Gem::Version
|
122
147
|
version: '0'
|
123
148
|
requirements: []
|
124
|
-
|
125
|
-
|
126
|
-
signing_key:
|
127
|
-
specification_version: 3
|
149
|
+
rubygems_version: 3.6.9
|
150
|
+
specification_version: 4
|
128
151
|
summary: Easy throttling for Ruby applications
|
129
|
-
test_files:
|
130
|
-
- spec/base_spec.rb
|
131
|
-
- spec/fixtures/throttling.yml
|
132
|
-
- spec/spec_helper.rb
|
133
|
-
- spec/throttling_spec.rb
|
134
|
-
- Guardfile
|
152
|
+
test_files: []
|
metadata.gz.sig
ADDED
Binary file
|
data/.gitignore
DELETED
data/Guardfile
DELETED
data/spec/base_spec.rb
DELETED
@@ -1,181 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Throttling do
|
4
|
-
before do
|
5
|
-
Throttling.reset_defaults!
|
6
|
-
@storage = Throttling.storage = TestStorage.new
|
7
|
-
end
|
8
|
-
|
9
|
-
describe 'instance methods' do
|
10
|
-
before do
|
11
|
-
Throttling.limits = { 'foo' => {'limit' => 5, 'period' => 2} }
|
12
|
-
@t = Throttling.for('foo')
|
13
|
-
end
|
14
|
-
|
15
|
-
{ :check_ip => '127.0.0.1', :check_user_id => 123 }.each do |check_method, valid_value|
|
16
|
-
describe check_method do
|
17
|
-
it 'should return true for nil check_values' do
|
18
|
-
@t.send(check_method, nil).should be_true
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'should return true if no limit specified in configs' do
|
22
|
-
Throttling.limits['foo']['limit'] = nil
|
23
|
-
@storage.should_receive(:fetch).and_return(1000)
|
24
|
-
@t.send(check_method, valid_value).should be_true
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'should return false if limit is 0' do
|
28
|
-
Throttling.limits['foo']['limit'] = 0
|
29
|
-
@storage.should_receive(:fetch).and_return(0)
|
30
|
-
@t.send(check_method, valid_value).should be_false
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'should raise an exception if no period specified in configs' do
|
34
|
-
Throttling.limits['foo']['period'] = nil
|
35
|
-
lambda { @t.send(check_method, valid_value) }.should raise_error(ArgumentError)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'should raise an exception if invalid period specified in configs' do
|
39
|
-
Throttling.limits['foo']['period'] = -1
|
40
|
-
lambda { @t.send(check_method, valid_value) }.should raise_error(ArgumentError)
|
41
|
-
|
42
|
-
Throttling.limits['foo']['period'] = 'foo'
|
43
|
-
lambda { @t.send(check_method, valid_value) }.should raise_error(ArgumentError)
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'should return true if throttling limit is not passed' do
|
47
|
-
@storage.should_receive(:fetch).and_return(1)
|
48
|
-
@t.send(check_method, valid_value).should be_true
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'should return false if throttling limit is passed' do
|
52
|
-
@storage.should_receive(:fetch).and_return(Throttling.limits['foo']['limit'] + 1)
|
53
|
-
@t.send(check_method, valid_value).should be_false
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'around limit' do
|
57
|
-
it 'should increase hit counter when values equals to limit - 1' do
|
58
|
-
@storage.should_receive(:fetch).and_return(Throttling.limits['foo']['limit'] - 1)
|
59
|
-
@storage.should_receive(:increment)
|
60
|
-
@t.send(check_method, valid_value)
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'should not increase hit counter when values equals to limit' do
|
64
|
-
@storage.should_receive(:fetch).and_return(Throttling.limits['foo']['limit'])
|
65
|
-
@storage.should_not_receive(:increment)
|
66
|
-
@t.send(check_method, valid_value)
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'should not increase hit counter when values equals to limit + 1' do
|
70
|
-
@storage.should_receive(:fetch).and_return(Throttling.limits['foo']['limit'] + 1)
|
71
|
-
@storage.should_not_receive(:increment)
|
72
|
-
@t.send(check_method, valid_value)
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'should allow exactly limit actions' do
|
76
|
-
5.times { @t.send(check_method, valid_value).should be_true }
|
77
|
-
@storage.should_not_receive(:increment)
|
78
|
-
@t.send(check_method, valid_value).should be_false
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
describe 'with multi-level limits' do
|
86
|
-
before do
|
87
|
-
Throttling.limits = { 'foo' => { 'two' => { 'limit' => 10, 'period' => 20 }, 'one' => { 'limit' => 5, 'period' => 2 } } }
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'should return false if at least one limit is reached' do
|
91
|
-
@storage.should_receive(:fetch).and_return(1, 100)
|
92
|
-
Throttling.for('foo').check_ip('127.0.0.1').should be_false
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'should return true if none limits reached' do
|
96
|
-
@storage.should_receive(:fetch).and_return(1, 2)
|
97
|
-
Throttling.for('foo').check_ip('127.0.0.1').should be_true
|
98
|
-
end
|
99
|
-
|
100
|
-
it 'should sort limits by period' do
|
101
|
-
@storage.should_receive(:fetch).ordered.with(/\:one\:/, anything).and_return(0)
|
102
|
-
@storage.should_receive(:fetch).ordered.with(/\:two\:/, anything).and_return(0)
|
103
|
-
Throttling.for('foo').check_ip('127.0.0.1').should be_true
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'should return as soon as limit reached' do
|
107
|
-
@storage.should_receive(:fetch).ordered.with(/\:one\:/, anything).and_return(10)
|
108
|
-
@storage.should_not_receive(:fetch).with(/\:two\:/)
|
109
|
-
Throttling.for('foo').check_ip('127.0.0.1').should be_false
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
context 'with values specified' do
|
114
|
-
before do
|
115
|
-
Throttling.limits_config = File.expand_path('../fixtures/throttling.yml', __FILE__)
|
116
|
-
end
|
117
|
-
|
118
|
-
it 'should return value when limit is not reached' do
|
119
|
-
@storage.should_receive(:fetch).and_return(0)
|
120
|
-
Throttling.for('request_priority').check_ip('127.0.0.1').should == 10
|
121
|
-
@storage.should_receive(:fetch).and_return(4)
|
122
|
-
Throttling.for('request_priority').check_ip('127.0.0.1').should == 10
|
123
|
-
|
124
|
-
@storage.should_receive(:fetch).and_return(5)
|
125
|
-
Throttling.for('request_priority').check_ip('127.0.0.1').should == 15
|
126
|
-
@storage.should_receive(:fetch).and_return(14)
|
127
|
-
Throttling.for('request_priority').check_ip('127.0.0.1').should == 15
|
128
|
-
|
129
|
-
@storage.should_receive(:fetch).and_return(15)
|
130
|
-
Throttling.for('request_priority').check_ip('127.0.0.1').should == 20
|
131
|
-
@storage.should_receive(:fetch).and_return(99)
|
132
|
-
Throttling.for('request_priority').check_ip('127.0.0.1').should == 20
|
133
|
-
|
134
|
-
@storage.should_receive(:fetch).and_return(100)
|
135
|
-
Throttling.for('request_priority').check_ip('127.0.0.1').should == 25
|
136
|
-
@storage.should_receive(:fetch).and_return(1000)
|
137
|
-
Throttling.for('request_priority').check_ip('127.0.0.1').should == 25
|
138
|
-
end
|
139
|
-
|
140
|
-
it 'should increase hit counter' do
|
141
|
-
@storage.should_receive(:fetch).and_return(4)
|
142
|
-
@storage.should_receive(:increment)
|
143
|
-
Throttling.for('request_priority').check_ip('127.0.0.1')
|
144
|
-
|
145
|
-
@storage.should_receive(:fetch).and_return(1000)
|
146
|
-
@storage.should_receive(:increment)
|
147
|
-
Throttling.for('request_priority').check_ip('127.0.0.1')
|
148
|
-
end
|
149
|
-
|
150
|
-
it 'should return false when highest limit reached' do
|
151
|
-
Throttling.limits['request_priority'].delete('default_value')
|
152
|
-
@storage.should_receive(:fetch).and_return(1000)
|
153
|
-
Throttling.for('request_priority').check_ip('127.0.0.1').should be_false
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
context do
|
158
|
-
before do
|
159
|
-
Throttling.limits = { 'foo' => {'limit' => 5, 'period' => 86400} }
|
160
|
-
@timestamp = 1334261569
|
161
|
-
end
|
162
|
-
|
163
|
-
describe 'key name' do
|
164
|
-
it 'should include type, value, name, and period start' do
|
165
|
-
Timecop.freeze(Time.at(@timestamp)) do
|
166
|
-
Throttling.for('foo').check_ip('127.0.0.1')
|
167
|
-
end
|
168
|
-
@storage.values.keys.first.should == 'throttle:foo:ip:127.0.0.1:global:15442'
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
describe 'key expiration' do
|
173
|
-
it 'should calculate expiration time' do
|
174
|
-
Timecop.freeze(Time.at(@timestamp)) do
|
175
|
-
Throttling.for('foo').check_ip('127.0.0.1')
|
176
|
-
end
|
177
|
-
@storage.values.values.first[:expires_in].should == 13631
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
user_signup:
|
2
|
-
limit: 20
|
3
|
-
period: 3600
|
4
|
-
|
5
|
-
search_requests:
|
6
|
-
minutely:
|
7
|
-
limit: 300
|
8
|
-
period: 600
|
9
|
-
hourly:
|
10
|
-
limit: 1000
|
11
|
-
period: 3600
|
12
|
-
daily:
|
13
|
-
limit: 10000
|
14
|
-
period: 86400
|
15
|
-
|
16
|
-
request_priority:
|
17
|
-
period: 86400
|
18
|
-
default_value: 25
|
19
|
-
values:
|
20
|
-
high_priority:
|
21
|
-
limit: 5
|
22
|
-
value: 10
|
23
|
-
medium_priority:
|
24
|
-
limit: 15
|
25
|
-
value: 15
|
26
|
-
low_priority:
|
27
|
-
limit: 100
|
28
|
-
value: 20
|
data/spec/spec_helper.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'bundler/setup'
|
2
|
-
require 'throttling'
|
3
|
-
require 'timecop'
|
4
|
-
|
5
|
-
class TestStorage
|
6
|
-
attr_reader :values
|
7
|
-
|
8
|
-
def fetch(key, options = {}, &block)
|
9
|
-
@values ||= {}
|
10
|
-
value = @values.fetch(key, &block)
|
11
|
-
value = { :value => value.to_s } unless Hash === value
|
12
|
-
@values[key] = value.merge(options)
|
13
|
-
value[:value]
|
14
|
-
end
|
15
|
-
|
16
|
-
def increment(key)
|
17
|
-
@values ||= {}
|
18
|
-
@values[key] ||= { :value => 0 }
|
19
|
-
@values[key][:value] = (@values[key][:value].to_i + 1).to_s
|
20
|
-
end
|
21
|
-
end
|