limited 0.0.1 → 0.1.0

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
  SHA1:
3
- metadata.gz: d9760e85bf1a3b2c89385bdcbd8076ff30e0cbd4
4
- data.tar.gz: f1bec62f986e779c66f70a01c06e6e8136de4861
3
+ metadata.gz: e0c628b05e6dd3db011e8ae8aa7299da4ab0664a
4
+ data.tar.gz: c5185402f1a9f258c641276f36e640994b17c10e
5
5
  SHA512:
6
- metadata.gz: 7c0936a592fd48b030c5bbaac2851fc058c0ba8e4242de3efe0a710704f37e28c7a8dfe86384800095834e9c425eee87453e43972176ca59508b527d719a79ce
7
- data.tar.gz: 1e39cf4620ba29bd06a3bf471fb43e52ccfd5592ba2016028b47fbdd2dd6bb6f17c3d4936abcf2edd0d9c8e783a09f97e2dda37a83ab2eb21cead18dd9cc8a27
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
- action :login, 20
28
- action :sending_contact_email, 123
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>`executed`</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>`limit_reached`</td>
54
+ <td>limit_reached</td>
50
55
  <td>Wheter the limit has been reached or not</td>
51
56
  </tr>
52
57
  </table>
@@ -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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Limited
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/limited.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "limited/version"
2
2
  require "limited/action"
3
+ require "limited/interval"
3
4
 
4
5
  module Limited
5
6
  # a hash containing all the
@@ -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
@@ -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
@@ -0,0 +1,12 @@
1
+ class Time #:nodoc:
2
+ class <<self
3
+ attr_accessor :testing_offset
4
+ alias_method :real_now, :now
5
+ def now
6
+ real_now + testing_offset
7
+ end
8
+ alias_method :new, :now
9
+ end
10
+ end
11
+
12
+ Time.testing_offset = 0
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.1
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-09 00:00:00.000000000 Z
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