fine_tune 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby-version +1 -0
- data/.travis.yml +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +86 -18
- data/fine_tune.gemspec +5 -4
- data/lib/fine_tune/base.rb +87 -15
- data/lib/fine_tune/max_rate_error.rb +1 -0
- data/lib/fine_tune/strategies/base.rb +20 -2
- data/lib/fine_tune/strategies/leaky_bucket.rb +126 -0
- data/lib/fine_tune/version.rb +1 -1
- data/lib/fine_tune.rb +14 -2
- metadata +56 -27
- checksums.yaml +0 -7
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p194
|
data/.travis.yml
CHANGED
data/LICENSE.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2016
|
3
|
+
Copyright (c) 2016 Vesess Inc. (laknath [at] vesess [dot] com)
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -1,41 +1,109 @@
|
|
1
|
-
|
1
|
+
[![Build Status](https://travis-ci.org/laknath/fine_tune.svg?branch=master)](https://travis-ci.org/laknath/fine_tune)
|
2
2
|
|
3
|
-
|
3
|
+
# FineTune - a flexible rate limiting/monitoring library
|
4
4
|
|
5
|
-
|
5
|
+
FineTune can be used to limit the rate of any given activity/event
|
6
|
+
stream such as outgoing emails for a user. It's not fixed on a
|
7
|
+
particular implementation of throttling, rather it provides a framework
|
8
|
+
to add custom implementations/algorithms.
|
9
|
+
|
10
|
+
The main difference with other rate limiting libraries is the
|
11
|
+
flexibility of using custom throttling algorithms and external
|
12
|
+
data/cache stores. Also it doesn't need to be configured first, all
|
13
|
+
options can be passed in each throttling request.
|
6
14
|
|
7
15
|
## Installation
|
8
16
|
|
9
|
-
|
17
|
+
$ gem install fine_tune
|
10
18
|
|
11
|
-
|
12
|
-
gem 'fine_tune'
|
13
|
-
```
|
19
|
+
## Usage
|
14
20
|
|
15
|
-
|
21
|
+
Throttle represents an occurrence of a defined event. It will return true
|
22
|
+
if the threshold has reached. If not the count will be increased by one
|
23
|
+
and will return false.
|
16
24
|
|
17
|
-
|
25
|
+
```
|
26
|
+
$ FineTune.throttle(:transactions_per_hour, user_id, options)
|
27
|
+
```
|
18
28
|
|
19
|
-
|
29
|
+
* :transactions_per_hour - the name for the event
|
30
|
+
* user_id - some unique identifier for a resource (ie: user)
|
31
|
+
* options - configurations for the throttling implementation
|
20
32
|
|
21
|
-
|
33
|
+
An additional block can be passed, which will be called with event
|
34
|
+
details. ie:
|
22
35
|
|
23
|
-
|
36
|
+
```
|
37
|
+
$ FineTune.throttle(:transactions_per_hour, user_id, options) do
|
38
|
+
| count, comparison, id, strategy, options |
|
39
|
+
#something...
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
In addition, there's a more charged version - throttle! which will raise
|
44
|
+
a MaxRateError when reached the threshold. Same as throttle, it takes a
|
45
|
+
block.
|
24
46
|
|
25
|
-
|
47
|
+
```
|
48
|
+
$ FineTune.throttle!(:transactions_per_hour, user_id, options)
|
49
|
+
```
|
26
50
|
|
27
|
-
|
51
|
+
To get the current count of events:
|
52
|
+
|
53
|
+
```
|
54
|
+
$ FineTune.count(:emails_per_day, user_id, options)
|
55
|
+
```
|
56
|
+
|
57
|
+
To check whether threshold has reached:
|
58
|
+
|
59
|
+
```
|
60
|
+
$ FineTune.rate_exceeded?(:emails_per_day, user_id, options)
|
61
|
+
```
|
62
|
+
|
63
|
+
To reset the count of events for a resource:
|
64
|
+
|
65
|
+
```
|
66
|
+
$ FineTune.reset(:emails_per_day, user_id, options)
|
67
|
+
```
|
68
|
+
|
69
|
+
###(Optional)
|
28
70
|
|
29
|
-
|
71
|
+
To configure default options:
|
30
72
|
|
31
|
-
|
73
|
+
```
|
74
|
+
$ FineTune.add_limit(:hourly_emails, window: 3600, limit: 25)
|
75
|
+
```
|
76
|
+
Options passed here will be overridden by any options passed in
|
77
|
+
the individual calls.
|
78
|
+
|
79
|
+
To remove a pre-defined rule:
|
80
|
+
|
81
|
+
```
|
82
|
+
$ FineTune.remove_limit(:hourly_emails, window: 3600, limit: 25)
|
83
|
+
```
|
84
|
+
|
85
|
+
###Supported implementations:
|
86
|
+
|
87
|
+
* [Leaky Bucket algorithm](https://en.wikipedia.org/wiki/Leaky_bucket)
|
88
|
+
|
89
|
+
|
90
|
+
## Development
|
91
|
+
|
92
|
+
After checking out the repo, run `bundle` to install dependencies. Then, run `rake test` to run the tests.
|
32
93
|
|
33
94
|
## Contributing
|
34
95
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
96
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/laknath/fine_tune. This project
|
97
|
+
is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to
|
98
|
+
the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
99
|
+
|
100
|
+
## Credits
|
101
|
+
|
102
|
+
Some of the behaviours were inspired by [Props](https://github.com/zendesk/prop).
|
103
|
+
|
104
|
+
Thanks you!
|
36
105
|
|
37
106
|
|
38
107
|
## License
|
39
108
|
|
40
109
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
41
|
-
|
data/fine_tune.gemspec
CHANGED
@@ -17,9 +17,10 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_development_dependency "bundler"
|
21
|
-
spec.add_development_dependency "rake"
|
20
|
+
spec.add_development_dependency "bundler"
|
21
|
+
spec.add_development_dependency "rake"
|
22
22
|
spec.add_development_dependency "minitest", "~> 5.0"
|
23
|
-
spec.add_development_dependency "minitest-reporters", "~> 1.1
|
24
|
-
spec.add_development_dependency "mocha", "~> 0.14
|
23
|
+
spec.add_development_dependency "minitest-reporters", "~> 1.1"
|
24
|
+
spec.add_development_dependency "mocha", "~> 0.14"
|
25
|
+
spec.add_development_dependency "activesupport", "~> 4.2"
|
25
26
|
end
|
data/lib/fine_tune/base.rb
CHANGED
@@ -1,15 +1,69 @@
|
|
1
|
+
#
|
2
|
+
# FineTune::Base provides a set of public methods and helpers to throttle
|
3
|
+
# and fetch the event count through a singleton object. The mechanism used to
|
4
|
+
# throttle and event counting is delegated to the defined strategy.
|
5
|
+
#
|
6
|
+
# Strategies can be defined for the entire library through
|
7
|
+
# FineTune.default_strategy=. Strategy can also be defined for each
|
8
|
+
# throttling call by passing <tt>:strategy</tt> option.
|
9
|
+
#
|
10
|
+
# Additionally, the datastore used to maintain event counts can be
|
11
|
+
# also customized by setting FineTune.adapter=. It takes an
|
12
|
+
# ActiveSupport::Cache::Store[http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html]
|
13
|
+
# as an adapter.
|
14
|
+
|
1
15
|
module FineTune
|
2
16
|
class Base
|
3
17
|
include Singleton
|
4
18
|
|
5
|
-
class << self;
|
19
|
+
class << self;
|
20
|
+
# Used to set a default strategy for all calls.
|
21
|
+
attr_accessor :default_strategy
|
22
|
+
|
23
|
+
# Used to set the external data store.
|
24
|
+
attr_accessor :adapter
|
25
|
+
|
26
|
+
def registry #:nodoc:
|
27
|
+
@@registry
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_strategy(strategy) #:nodoc:
|
31
|
+
registry.fetch(strategy, default_strategy).instance
|
32
|
+
end
|
33
|
+
end
|
6
34
|
|
35
|
+
# Currently supported strategies:
|
36
|
+
# * Leaky bucket algorithm
|
7
37
|
@@registry = {
|
8
38
|
:leaky_bucket => ::FineTune::Strategies::LeakyBucket,
|
9
39
|
:sliding_window => ::FineTune::Strategies::SlidingWindow
|
10
40
|
}
|
11
41
|
@default_strategy = ::FineTune::Strategies::LeakyBucket
|
12
42
|
|
43
|
+
# Calling throttle represents an occurance of an event.
|
44
|
+
# Depends on the strategy used for the actual calculations.
|
45
|
+
# Returns true if the event should be throttled.
|
46
|
+
#
|
47
|
+
# ==== Attributes
|
48
|
+
#
|
49
|
+
# * +name+ - Name of the rule ie: emails_sent
|
50
|
+
# * +id+ - Identifier for a specific resource ie: abc@example.com
|
51
|
+
# * +options+ - Options for the strategy. Depends on the strategy choosen
|
52
|
+
#
|
53
|
+
# ==== Options
|
54
|
+
#
|
55
|
+
# * +:strategy+ - The strategy chosen. Valid options are:
|
56
|
+
# - leaky_bucket
|
57
|
+
#
|
58
|
+
# Optionally, a block can be passed and it will be called with
|
59
|
+
# calculated values
|
60
|
+
#
|
61
|
+
# ==== Examples
|
62
|
+
# FineTune.throttle(:emails_sent, "abc@example.com",
|
63
|
+
# {strategy: :leaky_bucket}) do |count, comparison, key, strategy, options|
|
64
|
+
# # some calculation ...
|
65
|
+
# end
|
66
|
+
|
13
67
|
def throttle(name, id, options)
|
14
68
|
strategy, key, options = current_strategy(name, id, options)
|
15
69
|
count = strategy.increment(key, options)
|
@@ -20,46 +74,72 @@ module FineTune
|
|
20
74
|
comp >= 0
|
21
75
|
end
|
22
76
|
|
23
|
-
|
24
|
-
|
77
|
+
# The more forceful variant of throttle. Will raise a MaxRateError if the
|
78
|
+
# defined threshold is exceeds.
|
79
|
+
#
|
80
|
+
# Attributes and options are same as for +throttle+.
|
25
81
|
|
26
|
-
|
27
|
-
|
82
|
+
def throttle!(name, id, options)
|
83
|
+
throttle(name, id, options) do |count, comp, key, strategy, opts|
|
84
|
+
yield count, comp, key, strategy, opts if block_given?
|
28
85
|
|
29
|
-
raise MaxRateError.new(key, count, comp, strategy,
|
86
|
+
raise MaxRateError.new(key, count, comp, strategy, opts) if comp >= 0
|
30
87
|
end
|
31
88
|
end
|
32
89
|
|
90
|
+
# Returns true if current count exceeds the given limits.
|
91
|
+
#
|
92
|
+
# Attributes and options are same as for +throttle+.
|
33
93
|
def rate_exceeded?(name, id, options)
|
34
94
|
strategy, key, options = current_strategy(name, id, options)
|
35
95
|
strategy.compare?(strategy.count(key, options), options) >= 0
|
36
96
|
end
|
37
97
|
|
98
|
+
# Returns the current event count.
|
99
|
+
#
|
100
|
+
# Attributes and options are same as for +throttle+.
|
38
101
|
def count(name, id, options)
|
39
102
|
strategy, key, options = current_strategy(name, id, options)
|
40
103
|
strategy.count(key, options)
|
41
104
|
end
|
42
105
|
|
106
|
+
# Resets the counter for the given resource identifer to 0.
|
107
|
+
#
|
108
|
+
# Attributes and options are same as for +throttle+.
|
43
109
|
def reset(name, id, options)
|
44
110
|
strategy, key, options = current_strategy(name, id, options)
|
45
111
|
strategy.reset(key, options)
|
46
112
|
end
|
47
113
|
|
114
|
+
# Adds a preconfigured rule and options to the rule. These configs
|
115
|
+
# can be overridden by passing different option values later when
|
116
|
+
# calling +throttle+.
|
117
|
+
#
|
118
|
+
# ==== Attributes
|
119
|
+
#
|
120
|
+
# * +name+ - Name of the rule ie: emails_sent
|
121
|
+
# * +options+ - Default options for the rule
|
122
|
+
#
|
123
|
+
# ==== Examples
|
124
|
+
# FineTune.add_limit(:emails_sent_per_hour, {window: 3600})
|
125
|
+
#
|
48
126
|
def add_limit(name, options = {})
|
49
127
|
limits[name] ||= {}
|
50
128
|
limits[name].merge!(options)
|
51
129
|
end
|
52
130
|
|
131
|
+
# Removes a preconfigured rule and options.
|
53
132
|
def remove_limit(name)
|
54
133
|
limits.delete(name)
|
55
134
|
end
|
56
135
|
|
136
|
+
# Returns all default limits defined
|
57
137
|
def limits
|
58
138
|
@limits ||= {}
|
59
139
|
end
|
60
140
|
|
61
141
|
private
|
62
|
-
def current_strategy(name, id, options)
|
142
|
+
def current_strategy(name, id, options) #:nodoc:
|
63
143
|
options = (limits[name] || {}).merge(options)
|
64
144
|
strategy = self.class.find_strategy(options[:strategy])
|
65
145
|
|
@@ -71,13 +151,5 @@ module FineTune
|
|
71
151
|
|
72
152
|
[strategy, key, options]
|
73
153
|
end
|
74
|
-
|
75
|
-
def self.find_strategy(strategy)
|
76
|
-
registry.fetch(strategy, default_strategy).instance
|
77
|
-
end
|
78
|
-
|
79
|
-
def self.registry
|
80
|
-
@@registry
|
81
|
-
end
|
82
154
|
end
|
83
155
|
end
|
@@ -1,5 +1,11 @@
|
|
1
1
|
module FineTune
|
2
2
|
module Strategies
|
3
|
+
# This class is only for the purpose of extending and implementing
|
4
|
+
# own strategies. Any subclass should implement:
|
5
|
+
# * +identifier+
|
6
|
+
# * +increment+
|
7
|
+
# * +count+
|
8
|
+
# * +reset+
|
3
9
|
class Base
|
4
10
|
include Singleton
|
5
11
|
|
@@ -8,7 +14,15 @@ module FineTune
|
|
8
14
|
end
|
9
15
|
|
10
16
|
def build_key(name, id, options)
|
11
|
-
[name, id].flatten.join('
|
17
|
+
[:fine_tune, name, identifier, id].flatten.join('/')
|
18
|
+
end
|
19
|
+
|
20
|
+
def adapter
|
21
|
+
FineTune.adapter
|
22
|
+
end
|
23
|
+
|
24
|
+
def identifier
|
25
|
+
raise "not defined"
|
12
26
|
end
|
13
27
|
|
14
28
|
def increment(key, options)
|
@@ -20,7 +34,11 @@ module FineTune
|
|
20
34
|
end
|
21
35
|
|
22
36
|
def validate?(options)
|
23
|
-
|
37
|
+
if adapter.nil?
|
38
|
+
raise "no adapter given"
|
39
|
+
end
|
40
|
+
|
41
|
+
true
|
24
42
|
end
|
25
43
|
|
26
44
|
def reset(key, options)
|
@@ -1,6 +1,132 @@
|
|
1
1
|
module FineTune
|
2
2
|
module Strategies
|
3
|
+
|
4
|
+
# Implements {LeakyBucket algorithm}[https://en.wikipedia.org/wiki/Leaky_bucket]
|
5
|
+
# as a FineTune::Strategy.
|
3
6
|
class LeakyBucket < Base
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Set default values for window, average and maximum values so
|
10
|
+
# they won't need to be passed in every call
|
11
|
+
attr_accessor :default_window, :default_average, :default_maximum
|
12
|
+
end
|
13
|
+
|
14
|
+
# compares the count given and the burst value.
|
15
|
+
# Returns 0 if equal, -1 less and 1 greater
|
16
|
+
#
|
17
|
+
# ====== Attributes
|
18
|
+
#
|
19
|
+
# * +count+ - the current count
|
20
|
+
# * +options+ - Optional if default_maximum is set
|
21
|
+
#
|
22
|
+
# ====== Options
|
23
|
+
# * +:maximum - can be used to override default_maximum
|
24
|
+
def compare?(count, options)
|
25
|
+
count <=> maximum(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the current count incremented by one if it does not exceed the
|
29
|
+
# maximum value given. If it exceeds the maximum value, then it returns
|
30
|
+
# the maximum value.
|
31
|
+
#
|
32
|
+
# ====== Attributes
|
33
|
+
#
|
34
|
+
# * +key+ - the identifier for the resource in cache store
|
35
|
+
# * +options+ - Optional if default_maximum, default_window and default_average set
|
36
|
+
#
|
37
|
+
# ====== Options
|
38
|
+
# * +:maximum+ - can be used to override default_maximum ie: 20
|
39
|
+
# * +:average+ - can be used to override default_average ie: 10
|
40
|
+
# * +:window+ - can be used to override default_window ie: 3600 (one hour)
|
41
|
+
def increment(key, options)
|
42
|
+
count = count(key, options)
|
43
|
+
count = [maximum(options), count + 1].min
|
44
|
+
adapter.write(key, {count: count, timestamp: Time.now.to_i})
|
45
|
+
count
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the current count after discounting for the time since
|
49
|
+
# last access.
|
50
|
+
#
|
51
|
+
# Attributes and options are same as +increment+
|
52
|
+
def count(key, options)
|
53
|
+
resource = adapter.fetch(key, options) do
|
54
|
+
zero_counts
|
55
|
+
end
|
56
|
+
|
57
|
+
loss = loss(Time.now.to_i, resource[:timestamp], average(options),
|
58
|
+
window(options))
|
59
|
+
count = resource[:count] - loss
|
60
|
+
count > 0 ? count : 0
|
61
|
+
end
|
62
|
+
|
63
|
+
# Validates the options given. Window, average and maximum values must be set
|
64
|
+
# either through default values or the options
|
65
|
+
#
|
66
|
+
# Options are same as +increment+
|
67
|
+
def validate?(options)
|
68
|
+
super(options)
|
69
|
+
error = nil
|
70
|
+
window, average, maximum = window(options), average(options), maximum(options)
|
71
|
+
|
72
|
+
error = if !window || !average || !maximum
|
73
|
+
"window, average and maximum options are required"
|
74
|
+
elsif !positive_integer?(window)
|
75
|
+
"time window must be a positive integer"
|
76
|
+
elsif !non_negative_numeric?(average)
|
77
|
+
"average should be non negative numbers"
|
78
|
+
elsif maximum < average
|
79
|
+
"maximum should not be less than the average"
|
80
|
+
end
|
81
|
+
|
82
|
+
raise ArgumentError.new(error) if error
|
83
|
+
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
# Reset the count of the resource identified by the key to 0.
|
88
|
+
#
|
89
|
+
# ====== Attributes
|
90
|
+
#
|
91
|
+
# * +key+ - the identifier for the resource in cache store
|
92
|
+
def reset(key, options)
|
93
|
+
adapter.write(key, zero_counts)
|
94
|
+
end
|
95
|
+
|
96
|
+
def identifier #:nodoc:
|
97
|
+
:leaky_bucket
|
98
|
+
end
|
99
|
+
|
100
|
+
# calculates the loss given an average, window and a time interval
|
101
|
+
def loss(now, last_accessed, average, window)
|
102
|
+
loss = ((now - last_accessed) * average).to_f/window
|
103
|
+
loss > 0 ? loss : 0
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
def zero_counts #:nodoc:
|
108
|
+
{count: 0, timestamp: Time.now.to_i}
|
109
|
+
end
|
110
|
+
|
111
|
+
def window(options) #:nodoc:
|
112
|
+
options[:window] || self.class.default_window
|
113
|
+
end
|
114
|
+
|
115
|
+
def average(options) #:nodoc:
|
116
|
+
options[:average] || self.class.default_average
|
117
|
+
end
|
118
|
+
|
119
|
+
def maximum(options) #:nodoc:
|
120
|
+
options[:maximum] || options[:limit] || self.class.default_maximum
|
121
|
+
end
|
122
|
+
|
123
|
+
def positive_integer?(e) #:nodoc:
|
124
|
+
e.is_a?(Integer) && e > 0
|
125
|
+
end
|
126
|
+
|
127
|
+
def non_negative_numeric?(e) #:nodoc:
|
128
|
+
e.is_a?(Numeric) && e >= 0
|
129
|
+
end
|
4
130
|
end
|
5
131
|
end
|
6
132
|
end
|
data/lib/fine_tune/version.rb
CHANGED
data/lib/fine_tune.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Laknath Semage (blaknath at gmail dot com)
|
3
|
+
# Copyright:: Copyright (c) 2016 Vesess Inc.
|
4
|
+
# License:: MIT License
|
5
|
+
|
1
6
|
require "fine_tune/version"
|
2
7
|
require 'singleton'
|
3
8
|
require "forwardable"
|
@@ -8,11 +13,18 @@ end
|
|
8
13
|
require "fine_tune/base"
|
9
14
|
require "fine_tune/max_rate_error"
|
10
15
|
|
16
|
+
##
|
17
|
+
# FineTune helps throttle any kind of event sequence. It also supports
|
18
|
+
# custom throttling strategies using a common API. By default
|
19
|
+
# it supports LeakyBucket[https://en.wikipedia.org/wiki/Leaky_bucket] algorithm.
|
20
|
+
|
11
21
|
module FineTune
|
12
22
|
class << self
|
13
23
|
extend Forwardable
|
14
|
-
def_delegators :"FineTune::Base.instance", :
|
15
|
-
:
|
24
|
+
def_delegators :"FineTune::Base.instance", :count, :reset, :throttle,
|
25
|
+
:throttle!, :add_limit, :remove_limit, :rate_exceeded?
|
26
|
+
def_delegators :"FineTune::Base", :adapter, :adapter=, :default_strategy,
|
27
|
+
:default_strategy=
|
16
28
|
end
|
17
29
|
end
|
18
30
|
|
metadata
CHANGED
@@ -1,85 +1,112 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fine_tune
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
7
|
authors:
|
7
8
|
- Laknath Semage
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2016-
|
12
|
+
date: 2016-07-04 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: bundler
|
15
16
|
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
16
18
|
requirements:
|
17
|
-
- -
|
19
|
+
- - ! '>='
|
18
20
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
21
|
+
version: '0'
|
20
22
|
type: :development
|
21
23
|
prerelease: false
|
22
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
23
26
|
requirements:
|
24
|
-
- -
|
27
|
+
- - ! '>='
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
29
|
+
version: '0'
|
27
30
|
- !ruby/object:Gem::Dependency
|
28
31
|
name: rake
|
29
32
|
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
30
34
|
requirements:
|
31
|
-
- -
|
35
|
+
- - ! '>='
|
32
36
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
37
|
+
version: '0'
|
34
38
|
type: :development
|
35
39
|
prerelease: false
|
36
40
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
37
42
|
requirements:
|
38
|
-
- -
|
43
|
+
- - ! '>='
|
39
44
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
45
|
+
version: '0'
|
41
46
|
- !ruby/object:Gem::Dependency
|
42
47
|
name: minitest
|
43
48
|
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
44
50
|
requirements:
|
45
|
-
- -
|
51
|
+
- - ~>
|
46
52
|
- !ruby/object:Gem::Version
|
47
53
|
version: '5.0'
|
48
54
|
type: :development
|
49
55
|
prerelease: false
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
51
58
|
requirements:
|
52
|
-
- -
|
59
|
+
- - ~>
|
53
60
|
- !ruby/object:Gem::Version
|
54
61
|
version: '5.0'
|
55
62
|
- !ruby/object:Gem::Dependency
|
56
63
|
name: minitest-reporters
|
57
64
|
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
58
66
|
requirements:
|
59
|
-
- -
|
67
|
+
- - ~>
|
60
68
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.1
|
69
|
+
version: '1.1'
|
62
70
|
type: :development
|
63
71
|
prerelease: false
|
64
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
65
74
|
requirements:
|
66
|
-
- -
|
75
|
+
- - ~>
|
67
76
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.1
|
77
|
+
version: '1.1'
|
69
78
|
- !ruby/object:Gem::Dependency
|
70
79
|
name: mocha
|
71
80
|
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
72
82
|
requirements:
|
73
|
-
- -
|
83
|
+
- - ~>
|
74
84
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.14
|
85
|
+
version: '0.14'
|
76
86
|
type: :development
|
77
87
|
prerelease: false
|
78
88
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
79
90
|
requirements:
|
80
|
-
- -
|
91
|
+
- - ~>
|
81
92
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.14
|
93
|
+
version: '0.14'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: activesupport
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '4.2'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '4.2'
|
83
110
|
description: A rate limiter without the worst case throttling of two times the defined
|
84
111
|
threshold.
|
85
112
|
email:
|
@@ -88,8 +115,9 @@ executables: []
|
|
88
115
|
extensions: []
|
89
116
|
extra_rdoc_files: []
|
90
117
|
files:
|
91
|
-
-
|
92
|
-
-
|
118
|
+
- .gitignore
|
119
|
+
- .ruby-version
|
120
|
+
- .travis.yml
|
93
121
|
- Gemfile
|
94
122
|
- LICENSE.txt
|
95
123
|
- README.md
|
@@ -105,25 +133,26 @@ files:
|
|
105
133
|
homepage: https://github.com/vesess/fine_tune
|
106
134
|
licenses:
|
107
135
|
- MIT
|
108
|
-
metadata: {}
|
109
136
|
post_install_message:
|
110
137
|
rdoc_options: []
|
111
138
|
require_paths:
|
112
139
|
- lib
|
113
140
|
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
none: false
|
114
142
|
requirements:
|
115
|
-
- -
|
143
|
+
- - ! '>='
|
116
144
|
- !ruby/object:Gem::Version
|
117
145
|
version: '0'
|
118
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
|
+
none: false
|
119
148
|
requirements:
|
120
|
-
- -
|
149
|
+
- - ! '>='
|
121
150
|
- !ruby/object:Gem::Version
|
122
151
|
version: '0'
|
123
152
|
requirements: []
|
124
153
|
rubyforge_project:
|
125
|
-
rubygems_version:
|
154
|
+
rubygems_version: 1.8.23
|
126
155
|
signing_key:
|
127
|
-
specification_version:
|
156
|
+
specification_version: 3
|
128
157
|
summary: Limits rate of a given event sequence with defined strategies.
|
129
158
|
test_files: []
|
checksums.yaml
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
---
|
2
|
-
SHA1:
|
3
|
-
metadata.gz: 084b2f0a33fb235fae26629c06c4d3ea49832456
|
4
|
-
data.tar.gz: 36428f60b499e91fca15c2208edbb93b2ae21ec2
|
5
|
-
SHA512:
|
6
|
-
metadata.gz: ef657f44b8ccc3516998568101b3192103f0591a81171875bf626718137d88ae2a79c804ac47893fa0d42abad4f34cebf3ee0dcad12db9ebfc52c2ed9d57d130
|
7
|
-
data.tar.gz: e41b1821a9e08b4d3323e7fc52a66ce353a3669946444c8316997a2988e5bca99dc0f93ef807f9a3c5e1131e7a98d4367f1555a6d2d2f6fb21829f304c9f3367
|