limited 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 +4 -4
- data/README.md +9 -4
- data/lib/limited/action.rb +35 -3
- data/lib/limited/config.rb +2 -2
- data/lib/limited/interval.rb +60 -0
- data/lib/limited/version.rb +1 -1
- data/lib/limited.rb +1 -0
- data/spec/limited_action_spec.rb +25 -0
- data/spec/limited_config_spec.rb +23 -1
- data/spec/limited_interval_spec.rb +77 -0
- data/spec/spec_helper.rb +12 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0c628b05e6dd3db011e8ae8aa7299da4ab0664a
|
4
|
+
data.tar.gz: c5185402f1a9f258c641276f36e640994b17c10e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a5dce139e3e678c083855773ac616ee638124fdce8d07dfa3c00ba1f5c2b634adfaefa43e36ca99f646f83d24addea085a1095a08904c6449cc2eeca6dbc2b4
|
7
|
+
data.tar.gz: bdcee5091e679f9cdd3f12bb05f9bd3df953d8f929b0921405091b8538e35921cf897eb720473a36492d2e1fe1321e1c0e32c67016e9fcf63a7b692078339851
|
data/README.md
CHANGED
@@ -23,9 +23,14 @@ Or install it yourself as:
|
|
23
23
|
You need to define your actions somewhere in your application like this:
|
24
24
|
|
25
25
|
Limited.configure do
|
26
|
+
# this action should only be 1337 times at most
|
26
27
|
action :name_of_action, 1337
|
27
|
-
|
28
|
-
|
28
|
+
|
29
|
+
# only 1 login all 10 seconds
|
30
|
+
action :login, 1, 10
|
31
|
+
|
32
|
+
# at maximum 123 contact emails a day
|
33
|
+
action :sending_contact_email, 123, :day
|
29
34
|
end
|
30
35
|
|
31
36
|
The second parameter given to action defines how many times the action
|
@@ -42,11 +47,11 @@ Here are a few commonly used methods which every Action provides:
|
|
42
47
|
|
43
48
|
<table>
|
44
49
|
<tr>
|
45
|
-
<td
|
50
|
+
<td>executed</td>
|
46
51
|
<td>Call this method everytime the specified action is being executed</td>
|
47
52
|
</tr>
|
48
53
|
<tr>
|
49
|
-
<td
|
54
|
+
<td>limit_reached</td>
|
50
55
|
<td>Wheter the limit has been reached or not</td>
|
51
56
|
</tr>
|
52
57
|
</table>
|
data/lib/limited/action.rb
CHANGED
@@ -8,8 +8,6 @@ module Limited
|
|
8
8
|
attr_reader :name
|
9
9
|
# the amount of times the action can be executed
|
10
10
|
attr_reader :limit
|
11
|
-
# the amount of times the action already has been executed
|
12
|
-
attr_reader :num_executed
|
13
11
|
|
14
12
|
##
|
15
13
|
# :call-seq:
|
@@ -20,7 +18,7 @@ module Limited
|
|
20
18
|
# === Example
|
21
19
|
#
|
22
20
|
# Limited::Action.new :do_stuff, 1000
|
23
|
-
def initialize(name, limit)
|
21
|
+
def initialize(name, limit, interval = nil)
|
24
22
|
raise ArgumentError.new("Limited::Action::name needs to be a Symbol") unless name.is_a?(Symbol)
|
25
23
|
raise ArgumentError.new("Limited::Action::limit needs to be an Integer") unless limit.is_a?(Integer)
|
26
24
|
raise ArgumentError.new("Limited::Action::limit needs to be positive") if limit < 0
|
@@ -28,12 +26,30 @@ module Limited
|
|
28
26
|
@name = name
|
29
27
|
@limit = limit
|
30
28
|
@num_executed = 0
|
29
|
+
@interval = Interval.new(interval.nil? ? :endless : interval)
|
30
|
+
check_new_interval
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# returns the interval for which the limit counts
|
35
|
+
def interval
|
36
|
+
check_new_interval
|
37
|
+
@interval
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# returns the amount of times the action already
|
42
|
+
# has been executed
|
43
|
+
def num_executed
|
44
|
+
check_new_interval
|
45
|
+
@num_executed
|
31
46
|
end
|
32
47
|
|
33
48
|
##
|
34
49
|
# returns how many times the action can be executed
|
35
50
|
# until the given limit is reached
|
36
51
|
def num_left
|
52
|
+
check_new_interval
|
37
53
|
@limit - @num_executed
|
38
54
|
end
|
39
55
|
|
@@ -42,6 +58,7 @@ module Limited
|
|
42
58
|
# the action gets executed
|
43
59
|
# so the internal counter is always up-to-date
|
44
60
|
def executed
|
61
|
+
check_new_interval
|
45
62
|
@num_executed += 1
|
46
63
|
end
|
47
64
|
|
@@ -49,7 +66,22 @@ module Limited
|
|
49
66
|
# wheter the limit of executions
|
50
67
|
# has been exceeded
|
51
68
|
def limit_reached
|
69
|
+
check_new_interval
|
52
70
|
@limit <= @num_executed
|
53
71
|
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
##
|
76
|
+
# checks if the interval
|
77
|
+
# passes and resets the number
|
78
|
+
# of executions so
|
79
|
+
# the limit can be reached again
|
80
|
+
def check_new_interval
|
81
|
+
if @interval.passed?
|
82
|
+
@interval.reset_start
|
83
|
+
@num_executed = 0
|
84
|
+
end
|
85
|
+
end
|
54
86
|
end
|
55
87
|
end
|
data/lib/limited/config.rb
CHANGED
@@ -11,8 +11,8 @@ module Limited
|
|
11
11
|
#
|
12
12
|
# raises an ArgumentError if the same name
|
13
13
|
# for an action is not unique
|
14
|
-
def self.action(name, limit)
|
15
|
-
action = Limited::Action.new(name, limit)
|
14
|
+
def self.action(name, limit, interval_length = nil)
|
15
|
+
action = Limited::Action.new(name, limit, interval_length)
|
16
16
|
raise ArgumentError.new("action with name :#{name.to_s} has already been added") if Limited.actions.has_key?(name)
|
17
17
|
Limited.actions[name] = action
|
18
18
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Limited
|
2
|
+
##
|
3
|
+
# This represents a timespan within a limit counts
|
4
|
+
# after this timespan ends the limit
|
5
|
+
# should be reset
|
6
|
+
class Interval
|
7
|
+
# the length of the interval in seconds
|
8
|
+
attr_reader :length
|
9
|
+
# a timestamp when the last interval started
|
10
|
+
attr_reader :last_start
|
11
|
+
|
12
|
+
# common lengths of the intervals
|
13
|
+
# and shortcuts for them
|
14
|
+
@@interval_lengths = {
|
15
|
+
second: 1,
|
16
|
+
minute: 60,
|
17
|
+
hour: 60 * 60,
|
18
|
+
day: 60 * 60 * 24,
|
19
|
+
endless: 1.0/0.0
|
20
|
+
}
|
21
|
+
|
22
|
+
##
|
23
|
+
# initializes an interval
|
24
|
+
# by either supplying the length of the
|
25
|
+
# interval in seconds as an Numeric
|
26
|
+
# or by supplying one of the symbols
|
27
|
+
# :second, :minute, :hour or :day
|
28
|
+
def initialize(length)
|
29
|
+
is_sym = length.is_a?(Symbol) and @@interval_lengths.has_key?(length)
|
30
|
+
raise ArgumentError.new("Limited::Interval.length needs to be a Numeric or one of the symbols :second, :minute, :hour or :day") unless length.is_a?(Numeric) or is_sym
|
31
|
+
@length = is_sym ? @@interval_lengths[length] : length
|
32
|
+
reset_start
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# start a new interval and
|
37
|
+
# reset the last_start variable
|
38
|
+
def reset_start
|
39
|
+
now = Time.now
|
40
|
+
@last_start = (now.to_f / @length).to_i * @length
|
41
|
+
@last_start = 0 if @last_start.is_a?(Float) and @last_start.nan?
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# calculate the amount of seconds remaining
|
46
|
+
# in seconds for the interval to end
|
47
|
+
#
|
48
|
+
# when the interval ended 0 is returned so
|
49
|
+
# this method never returns negative numbers
|
50
|
+
def time_left
|
51
|
+
[0, (last_start.to_f + length - Time.now.to_f)].max
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# wheter the interval has finished
|
56
|
+
def passed?
|
57
|
+
time_left == 0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/limited/version.rb
CHANGED
data/lib/limited.rb
CHANGED
data/spec/limited_action_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
1
3
|
require 'limited'
|
2
4
|
require 'limited/action'
|
3
5
|
|
@@ -7,6 +9,7 @@ describe Limited::Action do
|
|
7
9
|
|
8
10
|
it { should respond_to(:name) }
|
9
11
|
it { should respond_to(:limit) }
|
12
|
+
it { should respond_to(:interval) }
|
10
13
|
it { should respond_to(:num_executed) }
|
11
14
|
it { should respond_to(:num_left) }
|
12
15
|
|
@@ -72,4 +75,26 @@ describe Limited::Action do
|
|
72
75
|
@action.limit_reached.should be_true
|
73
76
|
end
|
74
77
|
end
|
78
|
+
|
79
|
+
describe "with an ending interval" do
|
80
|
+
before { @interval_action = Limited::Action.new :ending_interval, 5, :minute }
|
81
|
+
it "should be able to reach the limit before the interval ends" do
|
82
|
+
@interval_action.limit_reached.should be_false
|
83
|
+
@interval_action.executed
|
84
|
+
@interval_action.limit_reached.should be_false
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "after the limit has been reached" do
|
88
|
+
before { 5.times { @interval_action.executed } }
|
89
|
+
it { @interval_action.limit_reached.should be_true }
|
90
|
+
|
91
|
+
describe "after the interval passed" do
|
92
|
+
before { Time.testing_offset += @interval_action.interval.time_left }
|
93
|
+
it "should reset num_executed" do
|
94
|
+
@interval_action.num_executed.should be 0
|
95
|
+
@interval_action.limit_reached.should be_false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
75
100
|
end
|
data/spec/limited_config_spec.rb
CHANGED
@@ -7,10 +7,32 @@ describe Limited::Config do
|
|
7
7
|
|
8
8
|
it "should add an Limited::Action to Limited::actions when calling Limited::Config::action" do
|
9
9
|
expect do
|
10
|
-
Limited::Config.action :new, 123
|
10
|
+
Limited::Config.action :new, 123, :day
|
11
11
|
end.to change(Limited, :actions)
|
12
12
|
end
|
13
13
|
|
14
|
+
describe "the Action objects in the Limited::actions array" do
|
15
|
+
before(:all) do
|
16
|
+
Limited::Config.action :value_check, 123, :day
|
17
|
+
Limited::Config.action :value_check1, 3
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have the correct name" do
|
21
|
+
Limited.actions[:value_check].name.should eq :value_check
|
22
|
+
Limited.actions[:value_check1].name.should eq :value_check1
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should have the correct limit" do
|
26
|
+
Limited.actions[:value_check].limit.should eq 123
|
27
|
+
Limited.actions[:value_check1].limit.should eq 3
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should have the correct interval" do
|
31
|
+
Limited.actions[:value_check].interval.length.should eq 24 * 60 * 60
|
32
|
+
Limited.actions[:value_check1].interval.length.should eq 1.0/0.0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
14
36
|
it "should not be possible to add an action with the same name twice" do
|
15
37
|
expect do
|
16
38
|
Limited::Config.action :same_name, 123
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'limited'
|
3
|
+
|
4
|
+
describe Limited::Interval do
|
5
|
+
let(:infinity) { 1.0/0.0 }
|
6
|
+
let(:endless) { Limited::Interval.new(:endless) }
|
7
|
+
|
8
|
+
subject { Limited::Interval.new 234 }
|
9
|
+
|
10
|
+
it { should respond_to(:length) }
|
11
|
+
it { should respond_to(:last_start) }
|
12
|
+
it { should respond_to(:time_left) }
|
13
|
+
it { should respond_to(:reset_start) }
|
14
|
+
it { should respond_to(:passed?) }
|
15
|
+
|
16
|
+
|
17
|
+
describe "length" do
|
18
|
+
it "should be set by the constructor" do
|
19
|
+
i = Limited::Interval.new(123)
|
20
|
+
i.length.should eq 123
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "can also be set via symbols :second, :minute, :hour, :day" do
|
24
|
+
it { Limited::Interval.new(:second ).length.should eq 1 }
|
25
|
+
it { Limited::Interval.new(:minute ).length.should eq 60 }
|
26
|
+
it { Limited::Interval.new(:hour ).length.should eq 60 * 60 }
|
27
|
+
it { Limited::Interval.new(:day ).length.should eq 60 * 60 * 24 }
|
28
|
+
it { endless.length.should eq infinity }
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should raise error when no integer or symbol given" do
|
32
|
+
expect { Limited::Interval.new(nil) }.to raise_error(ArgumentError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "time_left" do
|
37
|
+
it "should return a value equal or lower than length" do
|
38
|
+
Limited::Interval.new(541).time_left.should be <= 541
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be infinite if interval has the length Infinify" do
|
42
|
+
endless.time_left.should eq infinity
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "reset_start" do
|
47
|
+
it "should be calculated to a time near Time.now which difference is shorter than length" do
|
48
|
+
c = Limited::Interval.new(100)
|
49
|
+
(Time.now.to_f - c.last_start.to_f).should be <= 100
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should set last_start to 0 when interval is endless" do
|
53
|
+
endless.last_start.should eq 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "passed" do
|
58
|
+
let(:i) { Limited::Interval.new(541) }
|
59
|
+
it "should return false if length seconds have not yet passed" do
|
60
|
+
i.passed?.should be_false
|
61
|
+
Time.testing_offset += i.time_left - 1
|
62
|
+
i.passed?.should be_false
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should return true when enough time passed" do
|
66
|
+
Time.testing_offset += i.time_left
|
67
|
+
i.time_left.should eq 0
|
68
|
+
i.passed?.should be_true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should always return false when interval is endless" do
|
72
|
+
endless.passed?.should be_false
|
73
|
+
Time.testing_offset += i.time_left
|
74
|
+
endless.passed?.should be_false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: limited
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Moritz Küttel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -81,11 +81,14 @@ files:
|
|
81
81
|
- lib/limited.rb
|
82
82
|
- lib/limited/action.rb
|
83
83
|
- lib/limited/config.rb
|
84
|
+
- lib/limited/interval.rb
|
84
85
|
- lib/limited/version.rb
|
85
86
|
- limited.gemspec
|
86
87
|
- spec/limited_action_spec.rb
|
87
88
|
- spec/limited_config_spec.rb
|
89
|
+
- spec/limited_interval_spec.rb
|
88
90
|
- spec/limited_spec.rb
|
91
|
+
- spec/spec_helper.rb
|
89
92
|
homepage: https://github.com/mkuettel/limited
|
90
93
|
licenses:
|
91
94
|
- MIT
|
@@ -115,4 +118,6 @@ summary: Some utility functions to help you limit some actions in your applicati
|
|
115
118
|
test_files:
|
116
119
|
- spec/limited_action_spec.rb
|
117
120
|
- spec/limited_config_spec.rb
|
121
|
+
- spec/limited_interval_spec.rb
|
118
122
|
- spec/limited_spec.rb
|
123
|
+
- spec/spec_helper.rb
|