ruby
77 lines · 2 tabs
Alex Kumar
Jan 2026
2 tabs
class RateLimiter
def initialize(key:, limit:, window:)
@key = "rate_limit:#{key}"
@limit = limit
@window = window
end
def allowed?
now = Time.current.to_i
window_start = now - @window
Redis.current.multi do |pipeline|
# Remove old entries
pipeline.zremrangebyscore(@key, '-inf', window_start)
# Add current request
pipeline.zadd(@key, now, "#{now}-#{SecureRandom.hex(8)}")
# Count requests in window
pipeline.zcard(@key)
# Set expiry
pipeline.expire(@key, @window + 1)
end.last <= @limit
end
def remaining
now = Time.current.to_i
window_start = now - @window
Redis.current.zremrangebyscore(@key, '-inf', window_start)
count = Redis.current.zcard(@key)
[@limit - count, 0].max
end
def reset_at
now = Time.current.to_i
window_start = now - @window
oldest = Redis.current.zrange(@key, 0, 0, with_scores: true).first
return now + @window unless oldest
oldest[1].to_i + @window
end
end
module ApiRateLimiting
extend ActiveSupport::Concern
included do
before_action :check_rate_limit
end
private
def check_rate_limit
identifier = current_user&.id || request.ip
limiter = RateLimiter.new(
key: "api:#{identifier}",
limit: 100,
window: 60
)
unless limiter.allowed?
response.set_header('X-RateLimit-Limit', '100')
response.set_header('X-RateLimit-Remaining', '0')
response.set_header('X-RateLimit-Reset', limiter.reset_at.to_s)
render json: { error: 'RATE_LIMIT_EXCEEDED' }, status: :too_many_requests
return
end
response.set_header('X-RateLimit-Limit', '100')
response.set_header('X-RateLimit-Remaining', limiter.remaining.to_s)
response.set_header('X-RateLimit-Reset', limiter.reset_at.to_s)
end
end
2 files · ruby
Explain with highlit
While Rack::Attack handles basic rate limiting, custom throttling logic gives fine-grained control over quotas, burst allowances, and per-feature limits. I implement a token bucket algorithm in Redis using sorted sets to track request timestamps per user/IP. Each request checks if the bucket has capacity and adds a timestamp; old timestamps beyond the window are pruned. This approach supports burst tolerance—users can make several requests rapidly as long as their average rate stays within limits. I expose remaining quota and reset time in response headers (X-RateLimit-Remaining, X-RateLimit-Reset) so clients can self-regulate. Different endpoints or subscription tiers get different bucket sizes.
Related snips
ruby
class CommentsController < ApplicationController
before_action :set_post
def create
@comment = @post.comments.build(comment_params)
System test: asserting Turbo Stream responses
rails
hotwire
turbo
by codesnips
4 tabs
ruby
class Post < ApplicationRecord
belongs_to :author, class_name: 'User'
has_many :comments, dependent: :destroy
scope :published, -> { where.not(published_at: nil).where('published_at <= ?', Time.current) }
scope :draft, -> { where(published_at: nil) }
ActiveRecord scopes for reusable query logic
rails
activerecord
patterns
by Alex Kumar
1 tab
ruby
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
jwt
authentication
api
by Kai Nakamura
2 tabs
ruby
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
rails
caching
http-caching
by Alex Kumar
1 tab
ruby
class PostsController < ApplicationController
def index
@posts = Post.includes(:author)
.order(created_at: :desc)
.page(params[:page])
.per(10)
Turbo Frames: infinite scroll with lazy-loading frame
rails
turbo
hotwire
by codesnips
4 tabs
ruby
require "csv"
class PeopleCsvStream
include Enumerable
HEADERS = %w[id full_name email signed_up_at plan].freeze
Resilient CSV Export as a Streamed Response
rails
performance
streaming
by codesnips
3 tabs
Share this code
Here's the card — post it anywhere.