Permalink
Browse files

Documentation

- Moved to YARD
- Wrote some actual documentation
  • Loading branch information...
1 parent ec9a064 commit 9bdfe020a0a227937cf3c28d72f020aba5ef5b0f @ejfinneran committed Oct 29, 2011
Showing with 73 additions and 16 deletions.
  1. +2 −1 Gemfile
  2. +4 −4 Gemfile.lock
  3. +1 −1 README.md
  4. +24 −8 Rakefile
  5. +42 −2 lib/ratelimit.rb
View
@@ -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
View
@@ -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)
@@ -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
@@ -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
View
@@ -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).
View
@@ -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
View
@@ -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
@@ -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
@@ -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
@@ -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

0 comments on commit 9bdfe02

Please sign in to comment.