merb_threshold 0.1.4
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.
- 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
|
+
|