0.0
The project is in a healthy, maintained state
A Rails gem that provides seamless integration with Lightrate API throttling using local token buckets and controller-level rate limiting.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

~> 2.0
~> 13.0
~> 3.0
~> 1.0
~> 0.21
~> 6.0
~> 3.0

Runtime

 Project Readme

Lightrate Rails

A Ruby on Rails integration gem for LightRate API throttling. This gem provides seamless integration with the LightRate API using controller before_actions to automatically throttle requests using local token buckets that automatically refill from the server when needed.

Features

  • Controller-Level Throttling: Use throttle_with_lightrate in any controller to enable automatic rate limiting
  • Flexible User Identification: Customize user identification per-controller with full access to controller context
  • Local Token Bucket Only: Uses only the local token bucket approach for efficient throttling with automatic server refills
  • Custom Exception Handling: Raises LightRateNoTokensAvailable when no tokens are available
  • Fine-Grained Control: Use :only or :except options to throttle specific actions
  • Controller Helpers: Manual token consumption methods for advanced use cases

Installation

Add this line to your application's Gemfile:

gem 'lightrate-rails'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install lightrate-rails

Configuration

Basic Setup

Required: Configure Lightrate in your Rails application initializer:

# config/initializers/lightrate_rails.rb

LightrateRails.configure do |config|
  # Required: Your LightRate API key
  config.api_key = ENV['LIGHTRATE_API_KEY']
  
  # Required: Your LightRate Application ID
  config.application_id = ENV['LIGHTRATE_APPLICATION_ID']

  # Optional: Enable/disable throttling globally (default: true)
  config.enabled = Rails.env.production?
  
  # Optional: Default bucket size (default: 5)
  config.default_local_bucket_size = 10
end

Note: You must provide both a valid API key and Application ID. The gem will raise a ConfigurationError if either is missing.

Advanced Configuration

LightrateRails.configure do |config|
  # Required: Your LightRate API key
  config.api_key = ENV['LIGHTRATE_API_KEY']
  
  # Required: Your LightRate Application ID
  config.application_id = ENV['LIGHTRATE_APPLICATION_ID']
  
  # Enable/disable throttling globally (default: true)
  config.enabled = true
  
  # Default local bucket size (default: 5)
  config.default_local_bucket_size = 10
  
  # API client configuration
  config.timeout = 30
  config.retry_attempts = 3
  config.logger = Rails.logger
end

Usage

Global Client Architecture

The gem creates a single global LightRate client instance during Rails initialization. This approach provides several benefits:

  • Efficiency: No client creation overhead on each request
  • Shared Token Buckets: Token buckets are shared across all requests, providing better rate limiting
  • Memory Efficiency: Single client instance reduces memory usage
  • Consistent State: All parts of your application use the same client configuration

Controller-Level Throttling

The gem automatically includes LightrateRails::ControllerHelper in all your controllers. To enable throttling, simply call throttle_with_lightrate in any controller:

Basic Usage

# Throttle all actions in this controller
class ApiController < ApplicationController
  throttle_with_lightrate
end

# Only throttle specific actions
class UsersController < ApplicationController
  throttle_with_lightrate only: [:create, :update, :destroy]
  
  def index; end    # NOT throttled
  def show; end     # NOT throttled
  def create; end   # Throttled
  def update; end   # Throttled
  def destroy; end  # Throttled
end

# Throttle all actions EXCEPT specific ones
class PostsController < ApplicationController
  throttle_with_lightrate except: [:index, :show]
  
  def index; end    # NOT throttled
  def show; end     # NOT throttled
  def create; end   # Throttled
  def update; end   # Throttled
  def destroy; end  # Throttled
end

# Disable throttling for this entire controller
class HealthController < ApplicationController
  skip_lightrate_throttling
  
  def status; end   # NOT throttled
  def ping; end     # NOT throttled
end

Custom User Identification

By default, the gem uses current_user.id to identify users. You can customize this per-controller:

# Use a different method on your user object
class ApiController < ApplicationController
  throttle_with_lightrate user_identifier: -> { current_user&.uuid }
end

# Use a symbol for a controller method
class ApiController < ApplicationController
  throttle_with_lightrate user_identifier: :get_api_user_id
  
  private
  
  def get_api_user_id
    request.headers['X-API-User-ID']
  end
end

# Combine multiple identifiers
class ApiController < ApplicationController
  throttle_with_lightrate user_identifier: -> {
    "#{current_user&.id}:#{current_organization&.id}"
  }
end

# Use API token for identification
class ApiV2Controller < ApplicationController
  throttle_with_lightrate user_identifier: -> { 
    current_api_user&.token 
  }
end

# Combine with :only option
class ComplexController < ApplicationController
  throttle_with_lightrate(
    only: [:create, :update],
    user_identifier: -> { request.headers['X-User-Token'] }
  )
end

Manual Token Management in Controllers

For advanced use cases, you can manually manage token consumption. The helper methods are automatically available in all controllers:

class ApiController < ApplicationController
  def expensive_operation
    # Check if tokens are available for current path before proceeding
    if lightrate_tokens_available?
      # Your expensive operation here
      perform_expensive_operation
    else
      render json: { error: 'Rate limit exceeded' }, status: 429
    end
  end
  
  def path_specific_operation
    # Manually consume a token for a specific path
    consume_lightrate_token_for_path(path: '/api/v1/special')
    
    # Your operation here
  end
  
  def current_path_operation
    # Consume a token for the current request path (most common use case)
    consume_lightrate_token_for_current_path
    
    # Your operation here
  end
  
  def conditional_operation
    # Use the helper to conditionally execute code
    if lightrate_tokens_available?
      # Execute expensive operation
      heavy_computation
    else
      # Fallback to lighter operation
      light_computation
    end
  end
  
  def custom_user_operation
    # Use a different user identifier
    consume_lightrate_token_for_current_path(
      user_identifier: current_user.uuid
    )
    
    # Your operation here
  end
  
  def specific_path_operation
    # Consume token for a specific path and method
    consume_lightrate_token_for_path(
      path: "/api/v1/special-endpoint",
      method: "POST",
      user_identifier: current_user.id
    )
    
    # Your operation here
  end
end

Controller Configuration Methods

Configure throttling behavior at the controller level:

Method Description Options
throttle_with_lightrate(options = {}) Enable throttling for this controller only: - Array of action names to throttle
except: - Array of action names to exclude
user_identifier: - Proc or Symbol for custom user identification
skip_lightrate_throttling Disable throttling for this entire controller None

Available Helper Methods

The gem provides several helper methods for manual token management. All methods share a single global LightRate client instance that is created during Rails initialization, ensuring efficient token bucket management across all requests.

Method Description Parameters
lightrate_tokens_available? Check if tokens are available for current path user_identifier
consume_lightrate_token_for_current_path Consume token for current request path user_identifier
consume_lightrate_token_for_path Consume token for specific path path, method, user_identifier

Most Common Use Cases:

  • lightrate_tokens_available? - Use this to check before expensive operations
  • consume_lightrate_token_for_current_path - Use this when you want to consume a token for the current request path
  • consume_lightrate_token_for_path - Use this for specific paths different from the current request

Exception Handling

The gem automatically handles rate limiting through controller callbacks:

  • HTML requests: Redirects with flash message
  • JSON requests: Returns 429 status with JSON error
  • XML requests: Returns 429 status with XML error

Customizing Throttling Responses

You can customize the throttling response by overriding the handle_rate_limit_exceeded method in your controllers:

class ApiController < ApplicationController
  throttle_with_lightrate

  private

  def handle_rate_limit_exceeded(exception)
    # Custom throttling response
    render json: {
      error: 'Rate Limited',
      message: 'You have exceeded the rate limit for this endpoint.',
      path: exception.path,
      user: exception.user_identifier,
      retry_after: 30
    }, status: 429
  end
end

Exception Classes

LightRateNoTokensAvailable

Raised when no tokens are available for a request.

begin
  consume_lightrate_tokens(operation: 'my_operation')
rescue LightrateRails::LightRateNoTokensAvailable => e
  puts "No tokens available for path: #{e.path}"
  puts "User: #{e.user_identifier}"
  puts "Response: #{e.response}"
end

Configuration Options

Option Type Default Description
api_key String nil Required - Your LightRate API key
application_id String nil Required - Your LightRate Application ID
enabled Boolean true Enable/disable throttling globally
default_local_bucket_size Integer 5 Default size for local token buckets
timeout Integer 30 API request timeout in seconds
retry_attempts Integer 3 Number of API retry attempts
logger Logger Rails.logger Logger for API requests

Note: User identification is now configured per-controller using the user_identifier option in throttle_with_lightrate. See Custom User Identification for details.

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/lightbourne-technologies/lightrate-rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the Lightrate Rails project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.