merb_threshold 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README +225 -0
- data/Rakefile +62 -0
- data/TODO +4 -0
- data/lib/merb_threshold.rb +35 -0
- data/lib/merb_threshold/controller/merb_controller.rb +452 -0
- data/lib/merb_threshold/frequency.rb +165 -0
- data/lib/merb_threshold/helpers/recaptcha_helper.rb +70 -0
- data/lib/merb_threshold/helpers/wait_helper.rb +56 -0
- data/lib/merb_threshold/merbtasks.rb +40 -0
- data/lib/merb_threshold/per.rb +13 -0
- data/lib/merb_threshold/recaptcha_client.rb +47 -0
- data/lib/merb_threshold/templates/_recaptcha_partial.html.erb +16 -0
- data/lib/merb_threshold/templates/_wait_partial.html.erb +3 -0
- data/spec/controller/merb_controller_spec.rb +271 -0
- data/spec/frequency_spec.rb +136 -0
- data/spec/helpers/recaptcha_helper_spec.rb +29 -0
- data/spec/helpers/wait_helper_spec.rb +32 -0
- data/spec/per_spec.rb +9 -0
- data/spec/recaptcha_client_spec.rb +35 -0
- data/spec/spec_helper.rb +56 -0
- metadata +90 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
describe Frequency do
|
2
|
+
before(:each) do
|
3
|
+
now = Time.now.to_i
|
4
|
+
@history = [
|
5
|
+
now - 301,
|
6
|
+
now - 121,
|
7
|
+
now - 61,
|
8
|
+
now - 31,
|
9
|
+
now - 20
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should be able to initialize a frequency' do
|
14
|
+
@freq = Frequency.new(5,30,:seconds)
|
15
|
+
@freq.should be_an_instance_of(Frequency)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should be able to initialize a frequency without the units' do
|
19
|
+
@freq1 = Frequency.new 1, 30, :seconds
|
20
|
+
@freq2 = Frequency.new 1, 30.seconds
|
21
|
+
|
22
|
+
@freq1.interval.should be(@freq2.interval)
|
23
|
+
@freq1.occurrence.should be(@freq2.occurrence)
|
24
|
+
@freq1.units.should == @freq2.units
|
25
|
+
@freq1.period.should be(@freq2.period)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should create frequencies properly when using floats' do
|
29
|
+
@freq = Frequency.new 1, 3.3, :minutes
|
30
|
+
@freq.period.to_i == 198
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should cast the units automatically if not provided' do
|
34
|
+
@freq = Frequency.new 5, 5.minutes
|
35
|
+
@freq.period.should == 300
|
36
|
+
@freq.interval.should == 5.0
|
37
|
+
@freq.units.should == :minutes
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should cast the units automatically if not provided' do
|
41
|
+
@freq = Frequency.new 5, 5.hours
|
42
|
+
@freq.units.should == :hours
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should provide #to_s' do
|
46
|
+
@freq = Frequency.new 5, 5.minutes
|
47
|
+
@freq.to_s == "5 times per 5.0 minutes"
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should be able to load a list of events' do
|
51
|
+
@freq = Frequency.new 5, 5, :minutes
|
52
|
+
@freq.load @history
|
53
|
+
|
54
|
+
@freq.events.length.should be(5)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should be able to determine the rate of events over a period in seconds' do
|
58
|
+
@freq = Frequency.new 1, 2, :minutes
|
59
|
+
@freq.load @history
|
60
|
+
|
61
|
+
# 3 fall within 2 minutes
|
62
|
+
projected_rate = 3 / 120.0
|
63
|
+
@freq.rate.should == projected_rate
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should always determine an event is not permissable when the period is zero' do
|
67
|
+
@freq = Frequency.new 1, 0
|
68
|
+
@freq2 = Frequency.new 1, 0, :seconds
|
69
|
+
@freq3 = Frequency.new 1, 0, :minutes
|
70
|
+
@freq4 = Frequency.new 0, 0
|
71
|
+
|
72
|
+
@freq.permit?.should be(false)
|
73
|
+
@freq2.permit?.should be(false)
|
74
|
+
@freq3.permit?.should be(false)
|
75
|
+
@freq4.permit?.should be(false)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should be able to determine if an additional event is permissable' do
|
79
|
+
@freq = Frequency.new 1, 2, :minutes
|
80
|
+
@freq.load [Time.now.to_i - 30]
|
81
|
+
@freq.permit?.should be(false)
|
82
|
+
|
83
|
+
@freq2 = Frequency.new 5, 5, :minutes
|
84
|
+
@freq2.load @history
|
85
|
+
@freq2.permit?.should be(true)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should be able to add a single event' do
|
89
|
+
@freq = Frequency.new 5, 2.minutes
|
90
|
+
@freq.add(Time.now.to_i).length.should be(1)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should be able to determine if there is no wait for an available resource' do
|
94
|
+
@freq = Frequency.new 5, 5.minutes
|
95
|
+
@freq.load @history
|
96
|
+
@freq.wait.should be(0)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should be able to determine the wait time in seconds when unavailable' do
|
100
|
+
@freq = Frequency.new 2, 1.minute
|
101
|
+
@freq.load @history
|
102
|
+
|
103
|
+
# Treshold is 1 minute
|
104
|
+
# Oldest happend 31 seconds ago
|
105
|
+
# in 29 more seconds it will fall out of the period (60 seconds)
|
106
|
+
@freq.wait.should be(29)
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should be able to flush the currently loaded events' do
|
110
|
+
@freq = Frequency.new 2, 1.minute
|
111
|
+
@freq.load @history
|
112
|
+
@freq.respond_to? :flush
|
113
|
+
@freq.flush
|
114
|
+
@freq.events.should be_empty
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should be able to load! events' do
|
118
|
+
@freq = Frequency.new 2, 1.minute
|
119
|
+
@freq.load @history
|
120
|
+
@freq.load @history
|
121
|
+
@freq.events.length.should be(10)
|
122
|
+
@freq.load!([Time.now.to_i])
|
123
|
+
@freq.events.length.should be(1)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should be able to determine the wait tiem in seconds when unavailable (part 2)' do
|
127
|
+
@freq = Frequency.new 2, 1.minute
|
128
|
+
@freq.load @history
|
129
|
+
@freq.flush
|
130
|
+
|
131
|
+
@freq.add(Time.now.to_i)
|
132
|
+
@freq.add(Time.now.to_i)
|
133
|
+
|
134
|
+
@freq.wait.should be(60)
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
describe Merb::Threshold::Helpers do
|
2
|
+
before do
|
3
|
+
class CaptchaController < Merb::Controller
|
4
|
+
register_threshold :index
|
5
|
+
GhettoSessionStore = {}
|
6
|
+
|
7
|
+
def session
|
8
|
+
GhettoSessionStore[params[:session_id]] ||={}
|
9
|
+
GhettoSessionStore[params[:session_id]]
|
10
|
+
end
|
11
|
+
|
12
|
+
def index
|
13
|
+
if !permit_access?(:index)
|
14
|
+
@partial =File.join(File.expand_path("."),"lib/merb_threshold/templates/recaptcha_partial")
|
15
|
+
captcha :partial => @partial, :partial_opts => {:format => :html}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should display a captcha if the threshold is exceeded' do
|
22
|
+
@response = dispatch_to(CaptchaController, :index,{
|
23
|
+
:session_id=>"display-captcha"
|
24
|
+
})
|
25
|
+
|
26
|
+
@response.should be_successful
|
27
|
+
@response.body.should =~ /api.recaptcha/
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
describe Merb::Threshold::Helpers do
|
2
|
+
before do
|
3
|
+
class WaitController < Merb::Controller
|
4
|
+
register_threshold :index, :limit => 1.per(30.seconds)
|
5
|
+
|
6
|
+
GhettoSessionStore = {}
|
7
|
+
def session
|
8
|
+
GhettoSessionStore[params[:session_id]] ||={}
|
9
|
+
GhettoSessionStore[params[:session_id]]
|
10
|
+
end
|
11
|
+
|
12
|
+
def index
|
13
|
+
if !permit_access?(:index)
|
14
|
+
wait :partial => File.join(
|
15
|
+
File.expand_path("."),"lib/merb_threshold/templates/wait_partial"
|
16
|
+
), :partial_opts => {:format => :html}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should display a wait message if the threshold is exceeded' do
|
23
|
+
dispatch_to(WaitController, :index,{:session_id=>"display-wait-msg"})
|
24
|
+
|
25
|
+
@response = dispatch_to(WaitController, :index,{
|
26
|
+
:session_id=>"display-wait-msg"
|
27
|
+
})
|
28
|
+
|
29
|
+
@response.should be_successful
|
30
|
+
@response.body.should =~ /This resource will be available in 30 seconds/
|
31
|
+
end
|
32
|
+
end
|
data/spec/per_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
include Merb::Threshold
|
2
|
+
describe RecaptchaClient do
|
3
|
+
it 'should provide API_SERVER' do
|
4
|
+
defined?(RecaptchaClient::API_SERVER).should ==('constant')
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'should provide API_SSL_SERVER' do
|
8
|
+
defined?(RecaptchaClient::API_SSL_SERVER).should ==('constant')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should provide API_VERIFY_SERVER' do
|
12
|
+
defined?(RecaptchaClient::API_VERIFY_SERVER).should ==('constant')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should provide accessors for a public key' do
|
16
|
+
RecaptchaClient.should respond_to :public_key
|
17
|
+
RecaptchaClient.should respond_to :public_key=
|
18
|
+
RecaptchaClient.public_key = "key"
|
19
|
+
RecaptchaClient.public_key.should == "key"
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should provide accessors for a private key' do
|
23
|
+
RecaptchaClient.should respond_to :private_key
|
24
|
+
RecaptchaClient.should respond_to :private_key=
|
25
|
+
RecaptchaClient.private_key = "key"
|
26
|
+
RecaptchaClient.private_key.should == "key"
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should be able to submit a captcha response' do
|
30
|
+
result = RecaptchaClient.solve("127.0.0.1","fake_challenge","fake_response")
|
31
|
+
result.should be_instance_of(Array)
|
32
|
+
result.first.should be(false)
|
33
|
+
result.last.should == ("invalid-site-private-key")
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'merb-helpers/time_dsl'
|
3
|
+
|
4
|
+
require 'merb-core'
|
5
|
+
require 'merb-core/test'
|
6
|
+
|
7
|
+
Merb::Config.use do |c|
|
8
|
+
c[:use_mutex] = false
|
9
|
+
c[:session_store] = 'cookie'
|
10
|
+
c[:session_secret_key] = 'f1e532d42a798511504907cf28d9c660fe51ac91'
|
11
|
+
c[:session_id_key] = '_merb_threshold_session_id'
|
12
|
+
c[:reload_classes] = false
|
13
|
+
end
|
14
|
+
|
15
|
+
Merb::Plugins.config[:merb_threshold] = {
|
16
|
+
# TODO remove global catpcha keys
|
17
|
+
:public_key => nil,
|
18
|
+
:private_key => nil,
|
19
|
+
:recaptcha => true,
|
20
|
+
:wait_partial => 'shared/wait_partial',
|
21
|
+
:captcha_partial => 'shared/recaptcha_partial'
|
22
|
+
}
|
23
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'merb_threshold')
|
24
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'merb_threshold','frequency')
|
25
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'merb_threshold','controller','merb_controller')
|
26
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'merb_threshold','recaptcha_client')
|
27
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'merb_threshold','helpers','recaptcha_helper')
|
28
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'merb_threshold','helpers','wait_helper')
|
29
|
+
|
30
|
+
module Merb
|
31
|
+
class Controller
|
32
|
+
include Merb::Threshold::Helpers
|
33
|
+
end
|
34
|
+
end
|
35
|
+
Numeric.send :include, Merb::Threshold::Per
|
36
|
+
|
37
|
+
include Merb::Threshold
|
38
|
+
|
39
|
+
class Hash
|
40
|
+
def to_json
|
41
|
+
result = "{"
|
42
|
+
result << self.map do |k, v|
|
43
|
+
if ! v.is_a?(String)
|
44
|
+
"\"#{k}\": #{v}"
|
45
|
+
else
|
46
|
+
"\"#{k}\": \"#{v}\""
|
47
|
+
end
|
48
|
+
end.join(", ")
|
49
|
+
result << "}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
RecaptchaClient.public_key = '6LcnNgUAAAAAABrFyM-LuC53axOzHbE27afV4gxP'
|
56
|
+
RecaptchaClient.private_key = '6LcnNgUAAAAAAEqnr9HP9ofrChZTcuTjl_0FFnxF'
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: merb_threshold
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Cory O'Daniel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-24 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: merb
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.0.7.1
|
24
|
+
version:
|
25
|
+
description: Merb plugin that provides resource access rate limits and captcha'ing
|
26
|
+
email: contact@coryodaniel.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README
|
33
|
+
- LICENSE
|
34
|
+
- TODO
|
35
|
+
files:
|
36
|
+
- LICENSE
|
37
|
+
- README
|
38
|
+
- Rakefile
|
39
|
+
- TODO
|
40
|
+
- lib/merb_threshold
|
41
|
+
- lib/merb_threshold/controller
|
42
|
+
- lib/merb_threshold/controller/merb_controller.rb
|
43
|
+
- lib/merb_threshold/frequency.rb
|
44
|
+
- lib/merb_threshold/helpers
|
45
|
+
- lib/merb_threshold/helpers/recaptcha_helper.rb
|
46
|
+
- lib/merb_threshold/helpers/wait_helper.rb
|
47
|
+
- lib/merb_threshold/merbtasks.rb
|
48
|
+
- lib/merb_threshold/per.rb
|
49
|
+
- lib/merb_threshold/recaptcha_client.rb
|
50
|
+
- lib/merb_threshold/templates
|
51
|
+
- lib/merb_threshold/templates/_recaptcha_partial.html.erb
|
52
|
+
- lib/merb_threshold/templates/_wait_partial.html.erb
|
53
|
+
- lib/merb_threshold.rb
|
54
|
+
- spec/controller
|
55
|
+
- spec/controller/merb_controller_spec.rb
|
56
|
+
- spec/frequency_spec.rb
|
57
|
+
- spec/helpers
|
58
|
+
- spec/helpers/recaptcha_helper_spec.rb
|
59
|
+
- spec/helpers/wait_helper_spec.rb
|
60
|
+
- spec/per_spec.rb
|
61
|
+
- spec/recaptcha_client_spec.rb
|
62
|
+
- spec/spec_helper.rb
|
63
|
+
has_rdoc: true
|
64
|
+
homepage: http://github.com/coryodaniel
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: "0"
|
81
|
+
version:
|
82
|
+
requirements: []
|
83
|
+
|
84
|
+
rubyforge_project: merb
|
85
|
+
rubygems_version: 1.3.1
|
86
|
+
signing_key:
|
87
|
+
specification_version: 2
|
88
|
+
summary: Merb plugin that provides resource access rate limits and captcha'ing
|
89
|
+
test_files: []
|
90
|
+
|