Skip to content

Commit

Permalink
Documentation
Browse files Browse the repository at this point in the history
- Moved to YARD
- Wrote some actual documentation
  • Loading branch information
ejfinneran committed Oct 29, 2011
1 parent ec9a064 commit 9bdfe02
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 16 deletions.
3 changes: 2 additions & 1 deletion Gemfile
Expand Up @@ -15,7 +15,8 @@ group :development do
gem "mock_redis", "~> 0.2.0"
gem "bundler", "~> 1.0.0"
gem "jeweler", "~> 1.6.4"
gem "rdoc"
gem "yard"
gem "rdiscount"
end

group :test do
Expand Down
8 changes: 4 additions & 4 deletions Gemfile.lock
Expand Up @@ -6,14 +6,12 @@ GEM
bundler (~> 1.0)
git (>= 1.2.5)
rake
json (1.6.1)
metaclass (0.0.1)
mocha (0.10.0)
metaclass (~> 0.0.1)
mock_redis (0.2.0)
rake (0.9.2)
rcov (0.9.10)
rdoc (3.9.4)
rdiscount (1.6.8)
redis (2.2.2)
redis-namespace (1.1.0)
redis (< 3.0.0)
Expand All @@ -23,6 +21,7 @@ GEM
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
timecop (0.3.5)
yard (0.7.3)

PLATFORMS
ruby
Expand All @@ -32,9 +31,10 @@ DEPENDENCIES
jeweler (~> 1.6.4)
mocha (~> 0.10)
mock_redis (~> 0.2.0)
rdoc
rdiscount
redis
redis-namespace
shoulda
simplecov
timecop
yard
2 changes: 1 addition & 1 deletion README.md
@@ -1,5 +1,5 @@
Ratelimit: Slow your roll
=========================
==========================

Ratelimit provides a way to rate limit actions across multiple servers using Redis. This is a port of RateLimit.js found [here](https://github.com/chriso/redback/blob/master/lib/advanced_structures/RateLimit.js) and inspired by [this post](http://chris6f.com/rate-limiting-with-redis).

Expand Down
32 changes: 24 additions & 8 deletions Rakefile
Expand Up @@ -34,12 +34,28 @@ end

task :default => :test

require 'rdoc/task'
RDoc::Task.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""

rdoc.rdoc_dir = 'rdoc'
rdoc.title = "ratelimit #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
namespace :doc do
project_root = File.dirname(__FILE__)
doc_destination = File.join(project_root, 'rdoc')

begin
require 'yard'
require 'yard/rake/yardoc_task'

YARD::Rake::YardocTask.new(:generate) do |yt|
yt.files = Dir.glob(File.join(project_root, 'lib', '**', '*.rb'))
yt.options = ['--output-dir', doc_destination, '--readme', 'README.md']
end
rescue LoadError
desc "Generate YARD Documentation"
task :generate do
abort "Please install the YARD gem to generate rdoc."
end
end

desc "Remove generated documenation"
task :clean do
rm_r doc_dir if File.exists?(doc_destination)
end

end
44 changes: 42 additions & 2 deletions lib/ratelimit.rb
Expand Up @@ -3,8 +3,16 @@

class Ratelimit

attr_reader :bucket_interval

# Create a RateLimit object.
#
# @param [String] key A name to uniquely identify this rate limit. For example, 'emails'
# @param [Integer] bucket_span Time span to track in seconds
# @param [Integer] bucket_interval How many seconds each bucket represents
# @param [Integer] bucket_expiry How long we keep data in each bucket before it is auto expired.
# @param [Redis] redis Redis instance to use. One is created if nothing is passed.
#
# @return [RateLimit] RateLimit instance
#
def initialize(key, bucket_span = 600, bucket_interval = 5, bucket_expiry = 1200, redis = nil)
@key = key
@bucket_span = bucket_span
Expand All @@ -14,6 +22,9 @@ def initialize(key, bucket_span = 600, bucket_interval = 5, bucket_expiry = 1200
@redis = redis
end

# Add to the counter for a given subject.
#
# @param [String] subject A unique key to identify the subject. For example, 'user@foo.com'
def add(subject)
bucket = get_bucket
subject = @key + ":" + subject
Expand All @@ -25,6 +36,10 @@ def add(subject)
end
end

# Returns the count for a given subject and interval
#
# @param [String] subject Subject for the count
# @param [Integer] interval How far back (in seconds) to retrieve activity.
def count(subject, interval)
bucket = get_bucket
interval = [interval, @bucket_interval].max
Expand All @@ -40,14 +55,39 @@ def count(subject, interval)
return counts.inject(0) {|a, i| a += i.to_i}
end

# Check if the rate limit has been exceeded.
#
# @param [String] subject Subject to check
# @param [Hash] options Options hash
# @option options [Integer] :interval How far back to retrieve activity.
# @option options [Integer] :threshold Maximum number of actions
def exceeded?(subject, options = {})
return count(subject, options[:interval]) >= options[:threshold]
end

# Check if the rate limit is within bounds
#
# @param [String] subject Subject to check
# @param [Hash] options Options hash
# @option options [Integer] :interval How far back to retrieve activity.
# @option options [Integer] :threshold Maximum number of actions
def within_bounds?(subject, options = {})
return !exceeded?(subject, options)
end

# Execute a block once the rate limit is within bounds
# *WARNING* This will block the current thread until the rate limit is within bounds.
#
# @param [String] subject Subject for this rate limit
# @param [Hash] options Options hash
# @option options [Integer] :interval How far back to retrieve activity.
# @option options [Integer] :threshold Maximum number of actions
# @yield The block to be run
#
# @example Send an email as long as we haven't send 5 in the last 10 minutes
# ratelimit.exec_with_threshold(email, [:threshold => 5, :interval => 600]) do
# send_another_email
# end
def exec_within_threshold(subject, options = {}, &block)
options[:threshold] ||= 30
options[:interval] ||= 30
Expand Down

0 comments on commit 9bdfe02

Please sign in to comment.