### Minitest Test Environment Setup Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/08-usage-examples.md Configure Bullet for the test environment with Minitest to log issues and fail tests on unoptimized queries. Includes setup for starting and ending requests within Minitest. ```ruby # config/environments/test.rb config.after_initialize do Bullet.enable = true Bullet.bullet_logger = true Bullet.raise = true end # test/test_helper.rb module ActiveSupport class TestCase def before_setup Bullet.start_request super end def after_teardown super Bullet.perform_out_of_channel_notifications if Bullet.notification? Bullet.end_request end end end ``` -------------------------------- ### Start Rails Server Source: https://github.com/flyerhzm/bullet/blob/main/README.md Starts the Rails development server. ```bash $ rails s ``` -------------------------------- ### Get Notification Body (Abstract Method Example) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Example implementation of the abstract `body` method for a subclass like NPlusOneQuery. This method should return a string containing the notification's body, including model class, associations, and recommended actions. ```ruby def body "#{klazz_associations_str}\n Add to your query: #{associations_str}" end # => "Post => [:comments]\n Add to your query: .includes([:comments])" ``` -------------------------------- ### Simulate Counter Cache Setup Source: https://github.com/flyerhzm/bullet/blob/main/README.md Controller configuration to demonstrate counter cache functionality. This setup is for illustrating the detection of counter cache issues. ```ruby def index @posts = Post.all respond_to do |format| format.html # index.html.erb format.xml { render :xml => @posts } end end ``` -------------------------------- ### Unused Eager Loading Log Example Source: https://github.com/flyerhzm/bullet/blob/main/README.md Example log entry for an unused eager loading detection. Suggests removing the eager loading from the query. ```log 2009-08-25 20:53:56[INFO] AVOID eager loading detected Post => [:comments]· Remove from your query: .includes([:comments]) 2009-08-25 20:53:56[INFO] Call stack ``` -------------------------------- ### Get Notification Title (Abstract Method Example) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Example implementation of the abstract `title` method for a subclass like NPlusOneQuery. This method should return a string representing the notification's title. ```ruby def title "USE eager loading #{@path ? "in #{@path}" : 'detected'}" end # => "USE eager loading detected" # or "USE eager loading in /posts" ``` -------------------------------- ### Example Usage of Registry Methods Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/04-registry-api.md Demonstrates the usage of `add`, `[]`, `delete`, `each`, `select`, `include?`, and `key?` methods with sample data. ```ruby registry = Bullet::Registry::Base.new # Add single value registry.add("Post:1", :comments) registry["Post:1"] # => Set(:comments) # Add multiple values as array registry.add("Post:1", [:author, :tags]) registry["Post:1"] # => Set(:comments, :author, :tags) # Add to another key registry.add("Post:2", :comments) registry.add("Post:1", :comments) result = registry["Post:1"] # => Set(:comments) registry.add("Post:1", :comments) registry.add("Post:2", :author) registry.each do |key, associations| puts "#{key}: #{associations.inspect}" end # Output: # Post:1: # # Post:2: # registry.add("Post:1", :comments) registry.delete("Post:1") registry["Post:1"] # => nil registry.add("Post:1", :comments) registry.add("Post:2", :author) result = registry.select { |key, _| key.include?("Post:1") } # => {"Post:1" => Set(:comments)} registry = Bullet::Registry::Base.new registry.add("Post:1", :comments) registry.include?("Post:1", :comments) # => true registry.include?("Post:1", :author) # => false registry.include?("Post:2", :comments) # => false registry = Bullet::Registry::Base.new registry.add("Post:1", :comments) registry.key?("Post:1") # => true registry.key?("Post:2") # => false ``` -------------------------------- ### Need Counter Cache Log Example Source: https://github.com/flyerhzm/bullet/blob/main/README.md Example log entry indicating that a counter cache is needed for a specific association. ```log 2009-09-11 09:46:50[INFO] Need Counter Cache Post => [:comments] ``` -------------------------------- ### Seed Database with Example Data Source: https://github.com/flyerhzm/bullet/blob/main/README.md Populates the database with sample Post and Comment records using the Rails console. ```ruby post1 = Post.create(:name => 'first') post2 = Post.create(:name => 'second') post1.comments.create(:name => 'first') post1.comments.create(:name => 'second') post2.comments.create(:name => 'third') post2.comments.create(:name => 'fourth') ``` -------------------------------- ### Constructor Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/04-registry-api.md Initializes a new instance of the Bullet::Registry::Base class. The registry starts as an empty Hash. ```APIDOC ## Constructor Initializes a new instance of the Bullet::Registry::Base class. ```ruby registry = Bullet::Registry::Base.new ``` **Return Type:** Bullet::Registry::Base instance **Initialization:** Creates empty Hash `@registry` **Example:** ```ruby registry = Bullet::Registry::Base.new # @registry = {} ``` ``` -------------------------------- ### N+1 Query Log Example Source: https://github.com/flyerhzm/bullet/blob/main/README.md Example log entry for an N+1 Query detection. Includes the detected issue and the call stack to help locate the problematic code. ```log 2009-08-25 20:40:17[INFO] USE eager loading detected: Post => [:comments]· Add to your query: .includes([:comments]) 2009-08-25 20:40:17[INFO] Call stack /Users/richard/Downloads/test/app/views/posts/index.html.erb:8:in `each' /Users/richard/Downloads/test/app/controllers/posts_controller.rb:7:in `index' ``` -------------------------------- ### RSpec Test Environment Setup Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/08-usage-examples.md Configure Bullet for the test environment with RSpec to log issues and fail tests on unoptimized queries. Includes setup for starting and ending requests within RSpec. ```ruby # config/environments/test.rb config.after_initialize do Bullet.enable = true Bullet.bullet_logger = true Bullet.raise = true # Fail tests on unoptimized queries end # spec/rails_helper.rb RSpec.configure do |config| config.before(:each) do Bullet.start_request end config.after(:each) do Bullet.perform_out_of_channel_notifications if Bullet.notification? Bullet.end_request end end ``` -------------------------------- ### Install Bullet Gem using Gem Command Source: https://github.com/flyerhzm/bullet/blob/main/README.md Use this command to install the Bullet gem directly from RubyGems. ```bash gem install bullet ``` -------------------------------- ### Example of N+1 Detection - NPlusOneQuery.call_association Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md This example demonstrates how accessing `post.comments` without preloading triggers the `call_association` method, leading to an N+1 query detection. ```ruby # This triggers call_association: posts = Post.find([1, 2]) # possible_objects posts.each do |post| post.comments # call_association here - not preloaded! end # Result: N+1 query detected ``` -------------------------------- ### Example of Counter Cache Suggestion Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md This example demonstrates how repeated calls to `.size` on an association can trigger a counter cache suggestion. It assumes `posts` are already registered as possible objects. ```ruby posts = Post.all # possible_objects posts.each do |post| post.comments.size # Called multiple times = counter cache candidate end ``` -------------------------------- ### Configure Bullet in Development Environment Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Example configuration for enabling Bullet, its logger, and console output in the development environment. This should be placed within `config/environments/development.rb`. ```ruby config.after_initialize do Bullet.enable = true Bullet.bullet_logger = true Bullet.console = true end ``` -------------------------------- ### Append Content to HTML Body Examples Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/05-rack-middleware.md Provides examples of using `append_to_html_body` to insert a script tag into an HTML string, demonstrating behavior with and without a closing tag. ```ruby html = "Content" footer = "" result = middleware.append_to_html_body(html, footer) # => "Content" html_no_body = "Content" result = middleware.append_to_html_body(html_no_body, footer) # => "Content" ``` -------------------------------- ### Example of Adding Possible Objects - NPlusOneQuery.add_possible_objects Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md This shows how Bullet internally calls `add_possible_objects` when a collection of objects, like `Post.all`, is fetched from the database. ```ruby # When this executes: posts = Post.all # Returns array of posts # Bullet internally calls: Bullet::Detector::NPlusOneQuery.add_possible_objects(posts) ``` -------------------------------- ### Install Bullet Gem Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/00-README.md Add the Bullet gem to your Gemfile for development and test environments. ```ruby # Gemfile group :development, :test do gem 'bullet' end ``` -------------------------------- ### RSpec Matcher for Testing N+1 Queries Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/08-usage-examples.md Use RSpec to verify that your code does not introduce N+1 queries. This example demonstrates how to start and end a request with Bullet and assert that no notifications are generated. ```ruby # Gemfile group :test do gem 'rspec-rails' gem 'bullet' end # spec/models/post_spec.rb RSpec.describe Post do describe '#comments' do it 'should not cause N+1 query' do Bullet.start_request posts = Post.includes(:comments).all posts.each { |post| post.comments } expect(Bullet.notification?).to be false Bullet.end_request end end end ``` -------------------------------- ### Example of Tracking Inversed Objects - NPlusOneQuery.add_inversed_object Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md This example shows how Bullet internally calls `add_inversed_object` when an association is accessed through an inverse relationship, such as accessing `comment.post` when `Comment` `belongs_to :post`. ```ruby # When a Comment is created with a post_id: comment = Comment.create(post_id: 1) # Accessing the inverse: post = comment.post # Loads from the inverse, no N+1 # Bullet internally calls: Bullet::Detector::NPlusOneQuery.add_inversed_object(comment, :post) ``` -------------------------------- ### Empty Response Body Examples Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/05-rack-middleware.md Demonstrates the usage of the `empty?` method with different response body inputs, showing true for an empty string and false for HTML content. ```ruby middleware.empty?([""]) # => true middleware.empty?(["..."]) # => false ``` -------------------------------- ### Example: Manual Request Wrapping Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Manually wrap code in Bullet detection by calling `start_request` and `end_request`. This ensures all queries within the block are monitored. ```ruby # Manually wrap code in Bullet detection Bullet.start_request begin # Your code here Post.includes(:comments).each { |post| post.comments.count } ensure Bullet.end_request end ``` -------------------------------- ### Rack Middleware Request/Response Flow Example Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/05-rack-middleware.md Illustrates the sequence of operations when a request passes through the Bullet::Rack middleware, including Bullet's start/end request lifecycle and response modification. ```ruby # A request flows through: status, headers, body = middleware.call(rack_env) # Bullet.start_request called # App generates response # If Bullet.notification? and can inject: # - Appends footer_note to body # - Appends inline notifications # - Appends XHR footer script # - Updates Content-Length # Bullet.end_request called in ensure ``` -------------------------------- ### Start Bullet Request Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Initialize Bullet for a new request. Sets up thread-local registries for tracking associations and queries. Must be called before any code you want Bullet to monitor. ```ruby Bullet.start_request ``` -------------------------------- ### N+1 Query Log Entry Source: https://github.com/flyerhzm/bullet/blob/main/README.md Example log entry from `log/bullet.log` detailing an N+1 query, including the model, association, and suggested fix. ```log 2010-03-07 14:12:18[INFO] N+1 Query in /posts Post => [:comments] Add to your finder: :include => [:comments] 2010-03-07 14:12:18[INFO] N+1 Query method call stack /home/flyerhzm/Downloads/test_bullet/app/views/posts/index.html.erb:14:in `_render_template__600522146_80203160_0' /home/flyerhzm/Downloads/test_bullet/app/views/posts/index.html.erb:11:in `each' /home/flyerhzm/Downloads/test_bullet/app/views/posts/index.html.erb:11:in `_render_template__600522146_80203160_0' /home/flyerhzm/Downloads/test_bullet/app/controllers/posts_controller.rb:7:in `index' ``` -------------------------------- ### Configure Log Rotation for Bullet Logs Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/08-usage-examples.md Set up log rotation for Bullet's log file to manage disk space effectively. This example configures daily rotation, keeping 7 compressed logs. ```bash # logrotate config /var/www/myapp/log/bullet.log { daily rotate 7 compress missingok notifempty create 0640 www-data www-data } ``` -------------------------------- ### Initialize Bullet Registries Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/04-registry-api.md Sets up all necessary registries as thread-local variables at the start of a request. This ensures each request has its own isolated state for tracking performance issues. ```ruby Thread.current.thread_variable_set(:bullet_object_associations, Bullet::Registry::Base.new) Thread.current.thread_variable_set(:bullet_call_object_associations, Bullet::Registry::Base.new) Thread.current.thread_variable_set(:bullet_possible_objects, Bullet::Registry::Object.new) Thread.current.thread_variable_set(:bullet_impossible_objects, Bullet::Registry::Object.new) Thread.current.thread_variable_set(:bullet_inversed_objects, Bullet::Registry::Base.new) Thread.current.thread_variable_set(:bullet_eager_loadings, Bullet::Registry::Association.new) Thread.current.thread_variable_set(:bullet_call_stacks, Bullet::Registry::CallStack.new) Thread.current.thread_variable_set(:bullet_counter_possible_objects, Bullet::Registry::Object.new) Thread.current.thread_variable_set(:bullet_counter_impossible_objects, Bullet::Registry::Object.new) ``` -------------------------------- ### Example Usage of String.bullet_class_name Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/07-integration-points.md Demonstrates how to use the `bullet_class_name` method to extract the class name from strings formatted as 'ClassName:ID'. ```ruby "Post:1".bullet_class_name # => "Post" "Post".bullet_class_name # => "Post" ``` -------------------------------- ### Configure Bullet to Detect Only N+1 Queries Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/08-usage-examples.md Customize Bullet's behavior within your application's configuration. This example shows how to enable Bullet globally, specifically enable N+1 query detection, and disable unused eager loading and counter cache detection. ```ruby config.after_initialize do Bullet.enable = true # Only care about N+1 queries Bullet.n_plus_one_query_enable = true Bullet.unused_eager_loading_enable = false Bullet.counter_cache_enable = false # Reduces overhead end ``` -------------------------------- ### Initialize Notification with Multiple Associations Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Example of initializing a Bullet::Notification::Base with multiple associations. It demonstrates how the base_class, associations, and path attributes are set on the notification object. ```ruby notification = Bullet::Notification::Base.new("Post", [:comments, :author], "/posts") # notification.base_class => "Post" # notification.associations => [:comments, :author] # notification.path => "/posts" ``` -------------------------------- ### Example of Adding Impossible Object - NPlusOneQuery.add_impossible_object Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md This illustrates Bullet's internal call to `add_impossible_object` when a single object, such as `Post.first`, is retrieved from the database. ```ruby # When this executes: post = Post.first # Single result # Bullet internally calls: Bullet::Detector::NPlusOneQuery.add_impossible_object(post) ``` -------------------------------- ### Get Warnings by Type Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Retrieve a hash containing all notifications organized by type (n+1 queries, unused eager loadings, counter caches). ```ruby warnings = Bullet.warnings ``` -------------------------------- ### Bullet.enable= and Bullet.enable? Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Control and check the global enablement status of the Bullet gem. Enabling Bullet automatically patches ORMs and starts detection. ```APIDOC ## Bullet.enable= ### Description Enable or disable Bullet globally. ### Method `Bullet.enable = value` ### Parameters #### Path Parameters (none) #### Query Parameters (none) #### Request Body - **enable** (Boolean) - Required - Whether to enable Bullet detection ### Request Example ```ruby Bullet.enable = true ``` ### Response #### Success Response (200) - **Boolean** - The new enablement status #### Response Example ```ruby true ``` ## Bullet.enable? / Bullet.enabled? ### Description Check if Bullet is currently enabled. ### Method `Bullet.enable?` or `Bullet.enabled?` ### Parameters #### Path Parameters (none) #### Query Parameters (none) #### Request Body (none) ### Request Example ```ruby if Bullet.enable? puts "Bullet is active" end ``` ### Response #### Success Response (200) - **Boolean** - True if Bullet is enabled, false otherwise #### Response Example ```ruby puts "Bullet is #{Bullet.enable? ? 'enabled' : 'disabled'}" ``` ``` -------------------------------- ### Get NPlusOneQuery Notification Body (Ruby) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Generates the main content of the N+1 query notification, including the model, associations, and a suggested `.includes()` clause. ```ruby body = notification.body ``` -------------------------------- ### Create Rails Application and Models Source: https://github.com/flyerhzm/bullet/blob/main/README.md Steps to set up a new Rails application and define basic models for demonstration purposes. ```bash $ rails new test_bullet $ cd test_bullet $ rails g scaffold post name:string $ rails g scaffold comment name:string post_id:integer $ bundle exec rails db:migrate ``` -------------------------------- ### Get Application Root Directory Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Retrieves the application root directory. It uses `Rails.root.to_s` if Rails is available, otherwise falls back to the current working directory. ```ruby root = Bullet.app_root ``` ```ruby log_path = "#{Bullet.app_root}/log/bullet.log" ``` -------------------------------- ### Example: Skipping Bullet in Controller Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Use `Bullet.skip` as an `around_action` in a controller to temporarily disable Bullet detection for specific actions, preventing false positives. ```ruby # In controller: skip for certain actions class ApplicationController < ActionController::Base around_action :skip_bullet, if: -> { defined?(Bullet) } def skip_bullet Bullet.skip { yield } end end ``` -------------------------------- ### Configure UniformNotifier Delegation Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Delegate notifier configurations to UniformNotifier for various services like Sentry, Slack, and error tracking platforms. Examples show how to enable specific notifiers or provide configuration details for complex ones like XMPP and Slack. ```ruby Bullet.sentry = true ``` ```ruby Bullet.alert = true ``` ```ruby Bullet.console = true ``` ```ruby Bullet.rails_logger = true ``` ```ruby Bullet.honeybadger = true ``` ```ruby Bullet.bugsnag = true ``` ```ruby Bullet.appsignal = true ``` ```ruby Bullet.airbrake = true ``` ```ruby Bullet.rollbar = true ``` ```ruby Bullet.xmpp = { account: '...', password: '...', receiver: '...' } ``` ```ruby Bullet.slack = { webhook_url: '...', channel: '#...', username: '...' } ``` ```ruby Bullet.opentelemetry = true ``` ```ruby Bullet.raise = true ``` -------------------------------- ### Unused Preload Associations Alert Source: https://github.com/flyerhzm/bullet/blob/main/README.md Example alert box indicating unused preload associations. It specifies the model and the association that was eagerly loaded but not used. ```text The request has unused preload associations as follows: model: Post => associations: [comment] The request has N+1 queries as follows: None ``` -------------------------------- ### Get Objects That Might Cause N+1 Queries Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md Retrieves a registry of objects loaded as a collection that could potentially lead to N+1 queries if not preloaded. The registry format is `{ ClassName => ["ClassName:id1", "ClassName:id2", ...] }`. ```ruby objects_registry = Bullet::Detector::Association.possible_objects ``` ```ruby possible = Bullet::Detector::Association.possible_objects if possible.include?("Post:1") puts "Post:1 is flagged as a possible N+1 source" end ``` -------------------------------- ### Set Notification URL Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Sets the request URL where the issue was detected. The format should be 'METHOD PATH', for example, 'GET /posts?page=1'. ```ruby notification.url = "POST /api/posts" ``` -------------------------------- ### Example Usage of Bullet Primary Key Value Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/07-integration-points.md Demonstrates how Bullet generates a string representation for composite primary keys. The `bullet_primary_key_value` method returns the joined primary keys, and `bullet_key` includes the class name. ```ruby user.bullet_primary_key_value # => "1,2024-01-01" user.bullet_key # => "User:1,2024-01-01" ``` -------------------------------- ### Per-Environment Bullet Settings Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/08-usage-examples.md Customize Bullet's behavior based on the environment. Examples include verbose logging for development, quiet logging with Slack notifications for staging, and strict error raising for testing. ```ruby # Development: verbose # config/environments/development.rb config.after_initialize do Bullet.enable = true Bullet.alert = true Bullet.console = true Bullet.bullet_logger = true end ``` ```ruby # Staging: quiet, log only # config/environments/staging.rb config.after_initialize do Bullet.enable = true Bullet.bullet_logger = true Bullet.slack = { webhook_url: ENV['SLACK_URL'] } end ``` ```ruby # Test: strict # config/environments/test.rb config.after_initialize do Bullet.enable = true Bullet.raise = true # Fail tests end ``` -------------------------------- ### Get Formatted Call Stack Messages Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Access the protected `call_stack_messages` method to get a newline-separated string of locations from the call stack. ```ruby stack = notification.send(:call_stack_messages) ``` -------------------------------- ### Initialize NPlusOneQuery Notification (Ruby) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Constructs a notification object for N+1 query detection. Requires call stack information, the base model class name, and associated models. ```ruby notification = Bullet::Notification::NPlusOneQuery.new(callers, "Post", [:comments]) ``` ```ruby callers = [ "/app/controllers/posts_controller.rb:7:in `index'", "/app/views/posts/index.html.erb:14:in `each'" ] notification = Bullet::Notification::NPlusOneQuery.new( callers, "Post", [:comments], "/posts" ) ``` -------------------------------- ### Thread-Local Storage in Ruby Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/00-README.md Demonstrates setting and getting thread-local variables in Ruby, which Bullet uses for thread safety in multi-threaded servers. Ensure you use `Bullet.pause`/`Bullet.resume` instead of toggling `Bullet.enable` at runtime. ```ruby Thread.current.thread_variable_set(:bullet_start, true) ``` ```ruby Thread.current.thread_variable_get(:bullet_notification_collector) ``` -------------------------------- ### Configure Bullet Logging in Production Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/08-usage-examples.md Enable Bullet and configure logging to a file (`log/bullet.log`) in production environments. Optionally, set up Slack notifications for critical alerts. ```ruby # config/environments/production.rb config.after_initialize do if ENV['BULLET_ENABLED'] == 'true' Bullet.enable = true Bullet.bullet_logger = true # Logs to log/bullet.log # Optional: Slack for critical issues Bullet.slack = { webhook_url: ENV['SLACK_WEBHOOK_URL'], channel: '#performance-alerts' } end end ``` -------------------------------- ### Bullet.start_request Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Initializes Bullet for a new request by setting up thread-local registries for tracking associations and queries. This method is essential for enabling Bullet's detection capabilities within a specific request context. ```APIDOC ## Bullet.start_request ### Description Initialize Bullet for a new request. Sets up thread-local registries for tracking associations and queries. ### Method ```ruby Bullet.start_request ``` ### Return Type nil ### Thread-Safety Safe - uses thread-local storage via `Thread.current.thread_variable_set` ### Registers - `:bullet_start` - Request started flag - `:bullet_notification_collector` - NotificationCollector instance - `:bullet_object_associations` - Registry::Base for preloaded associations - `:bullet_call_object_associations` - Registry::Base for called associations - `:bullet_possible_objects` - Registry::Object for potential N+1 objects - `:bullet_impossible_objects` - Registry::Object for objects that won't cause N+1 - `:bullet_inversed_objects` - Registry::Base for inversed associations - `:bullet_eager_loadings` - Registry::Association for eager loaded data - `:bullet_call_stacks` - Registry::CallStack for stack traces - `:bullet_counter_possible_objects` - Registry::Object for counter cache detection - `:bullet_counter_impossible_objects` - Registry::Object for counter cache ### Example ```ruby # Manually wrap code in Bullet detection Bullet.start_request begin # Your code here Post.includes(:comments).each { |post| post.comments.count } ensure Bullet.end_request end ``` ``` -------------------------------- ### Get Notification Path Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves the request path that was optionally set during the notification's initialization. ```ruby puts notification.path # => "/posts" ``` -------------------------------- ### Get Notification Associations Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves the array of association names that was set during the notification's initialization. ```ruby puts notification.associations.inspect # => [:comments, :author] ``` -------------------------------- ### Initialize Bullet::Rack Middleware Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/05-rack-middleware.md Instantiate the Bullet::Rack middleware, passing the next application or middleware in the Rack stack. ```ruby middleware = Bullet::Rack.new(app) ``` -------------------------------- ### Access Notification Collector Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Retrieve the notification collector for the current request. Returns nil if Bullet is not started. ```ruby collector = Bullet.notification_collector notifications = collector.collection ``` -------------------------------- ### Get Preloaded Object Associations Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md Retrieves all associations that have been preloaded on objects. This is useful for understanding the scope of preloading. ```ruby object_associations = Bullet::Detector::Association.send(:object_associations) ``` -------------------------------- ### Get Notification Base Class Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves the model class name that was set during the notification's initialization. ```ruby puts notification.base_class # => "Post" ``` -------------------------------- ### Initialize Bullet::Registry::Base Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/04-registry-api.md Creates a new instance of the registry. Initializes an empty hash to store registry entries. ```ruby registry = Bullet::Registry::Base.new ``` -------------------------------- ### Get Explicit Eager Loadings Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md Retrieves all associations that were explicitly eager loaded in queries. This is used by the UnusedEagerLoading detector. ```ruby eager_loadings = Bullet::Detector::Association.send(:eager_loadings) ``` -------------------------------- ### Get Full Notification Text Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Retrieve an array of strings containing the full text of all notifications, including associations and recommendations. ```ruby texts = Bullet.text_notifications ``` -------------------------------- ### Development Environment Configuration Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/08-usage-examples.md Configure Bullet for development to enable core detections and various notification methods. ```ruby # config/environments/development.rb config.after_initialize do Bullet.enable = true # Core detections Bullet.n_plus_one_query_enable = true Bullet.unused_eager_loading_enable = true Bullet.counter_cache_enable = true # Notifications Bullet.alert = true # JavaScript alert Bullet.bullet_logger = true # log/bullet.log Bullet.console = true # Browser console Bullet.rails_logger = true # Rails logger Bullet.add_footer = true # Footer widget Bullet.footer_position = 'bottom_right' end ``` -------------------------------- ### Bullet.n_plus_one_query_enable= and Bullet.n_plus_one_query_enable? Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Control and check the enablement status for N+1 query detection. ```APIDOC ## Bullet.n_plus_one_query_enable= ### Description Enable or disable N+1 query detection. ### Method `Bullet.n_plus_one_query_enable = value` ### Parameters #### Path Parameters (none) #### Query Parameters (none) #### Request Body - **enable** (Boolean) - Required - Whether to detect N+1 queries ### Request Example ```ruby Bullet.n_plus_one_query_enable = true ``` ### Response #### Success Response (200) - **Boolean** - The new enablement status #### Response Example ```ruby true ``` ## Bullet.n_plus_one_query_enable? ### Description Check if N+1 query detection is enabled. ### Method `Bullet.n_plus_one_query_enable?` ### Parameters #### Path Parameters (none) #### Query Parameters (none) #### Request Body (none) ### Request Example ```ruby if Bullet.n_plus_one_query_enable? puts "N+1 detection is active" end ``` ### Response #### Success Response (200) - **Boolean** - True if N+1 query detection is enabled, false otherwise #### Response Example ```ruby puts "N+1 detection is #{Bullet.n_plus_one_query_enable? ? 'active' : 'inactive'}" ``` ``` -------------------------------- ### Get Accessed Object Associations Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md Retrieves all associations that have been accessed on objects. This helps identify associations that were actually used after preloading. ```ruby call_object_associations = Bullet::Detector::Association.send(:call_object_associations) ``` -------------------------------- ### Bullet.footer_position Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Gets or sets the position of the footer notification widget on the page. Allows customization of where Bullet's UI elements appear. ```APIDOC ## Bullet.footer_position ### Description Get or set the position of the footer notification widget on the page. ### Method `Bullet.footer_position` (getter) or `Bullet.footer_position = position` (setter) ### Return Type String ### Valid Values 'bottom_left' (default), 'bottom_right', 'top_left', 'top_right' ### Default 'bottom_left' ### Example ```ruby Bullet.footer_position = 'bottom_right' ``` ``` -------------------------------- ### Get CounterCache Notification Title Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves the title for a CounterCache notification. This title indicates the need for a counter cache with Active Record. ```ruby notification.title ``` -------------------------------- ### Check if Bullet is Active Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Check if Bullet is currently active for the current request and thread. Returns true if Bullet is enabled and the request has been started. ```ruby if Bullet.start? puts "Bullet is actively detecting for this request" end ``` -------------------------------- ### Set Up Slack Notifications for Bullet Alerts Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/08-usage-examples.md Configure Bullet to send N+1 query notifications to a Slack channel. Ensure the SLACK_WEBHOOK_URL environment variable is set. ```ruby # config/environments/production.rb config.after_initialize do Bullet.enable = true Bullet.slack = { webhook_url: ENV['SLACK_WEBHOOK_URL'], channel: '#alerts', username: 'Bullet Bot' } # Only in production for severity Bullet.raise = false Bullet.bullet_logger = true end ``` -------------------------------- ### Configure Stack Trace Includes Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/06-types-and-configuration.md Define patterns for file paths to always include in stack traces. Supports string (substring), regex, and array (path with line range or method name) filters. ```ruby Bullet.stacktrace_includes = [ 'my_custom_gem', /^vendor\/special_code/, ['app/models/custom.rb', 16..30], ['app/services/api.rb', 'validate'] ] ``` -------------------------------- ### Manual Integration with start_request / end_request Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/07-integration-points.md Manually manage the request lifecycle using Bullet.start_request and Bullet.end_request. This approach is useful for wrapping larger scopes of code but requires manual lifecycle management. ```ruby Bullet.start_request begin # Your code here Post.includes(:comments).each { |p| p.comments.count } ensure Bullet.end_request end ``` -------------------------------- ### N+1 Query Detection Alert Source: https://github.com/flyerhzm/bullet/blob/main/README.md Example of the alert box shown by Bullet when an N+1 query is detected. It indicates the model and association involved. ```text The request has unused preload associations as follows: None The request has N+1 queries as follows: model: Post => associations: [comment] ``` -------------------------------- ### Enable OpenTelemetry Traces Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/06-types-and-configuration.md Set `Bullet.opentelemetry` to `true` to send traces to OpenTelemetry. Requires the OpenTelemetry gem and its configuration. ```ruby Bullet.opentelemetry = true ``` -------------------------------- ### Configure Notifier Integrations Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Set common notifier configurations to enable or disable specific delivery channels. Ensure webhook URLs and channels are correctly set for services like Slack. ```ruby Bullet.console = true Bullet.alert = true Bullet.sentry = true Bullet.slack = { webhook_url: 'https://hooks.slack.com/services/...', channel: '#alerts' } Bullet.bullet_logger = true Bullet.raise = true ``` -------------------------------- ### Check Key Existence (`key?`) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/04-registry-api.md Determines if a given key exists within the registry. ```APIDOC ## `key?` Check if a key exists in the registry. ```ruby if registry.key?(key) puts "Key found" end ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | key | String, Symbol, Array | Yes | Key to check | **Return Type:** Boolean **Example:** ```ruby registry = Bullet::Registry::Base.new registry.add("Post:1", :comments) registry.key?("Post:1") # => true registry.key?("Post:2") # => false ``` ``` -------------------------------- ### Get CounterCache Notification Body Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves the body of a CounterCache notification, which lists the model name and the associated associations that require a counter cache. ```ruby notification.body ``` -------------------------------- ### Bullet::Notification::NPlusOneQuery Constructor Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Initializes a notification object for N+1 query detection. ```APIDOC ## `Bullet::Notification::NPlusOneQuery.new` ### Description Constructor for N+1 query notification. ### Parameters #### Path Parameters - **callers** (Array) - Required - Stack trace showing where access occurred - **base_class** (String) - Required - Model class name - **associations** (Symbol, String, Array) - Required - Association name(s) - **path** (String) - Optional - Request path ### Example ```ruby callers = [ "/app/controllers/posts_controller.rb:7:in `index'", "/app/views/posts/index.html.erb:14:in `each'" ] notification = Bullet::Notification::NPlusOneQuery.new( callers, "Post", [:comments], "/posts" ) ``` ``` -------------------------------- ### Get Formatted Call Stack (Ruby) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves the formatted call stack messages for a notification. This method is protected and accessed using `send`. ```ruby stack = notification.send(:call_stack_messages) ``` ```ruby stack = notification.send(:call_stack_messages) # => "Call stack\n /app/controllers/posts_controller.rb:7:in `index'\n /app/views/posts/index.html.erb:14:in `each'" ``` -------------------------------- ### Iterate Entries (`each`) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/04-registry-api.md Iterates over all key-value pairs in the registry. Yields each key and its associated value to a block. ```APIDOC ## `each` Iterate over all registry entries. ```ruby registry.each { |key, value| ... } ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | block | Block | Yes | Block yielding (key, value) pairs | **Return Type:** Hash (the registry) **Yields:** `|key, value|` for each entry **Example:** ```ruby registry = Bullet::Registry::Base.new registry.add("Post:1", :comments) registry.add("Post:2", :author) registry.each do |key, associations| puts "#{key}: #{associations.inspect}" end # Output: # Post:1: # # Post:2: # ``` ``` -------------------------------- ### Get NPlusOneQuery Notification Title (Ruby) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves the formatted title for an N+1 query notification. The title indicates whether a path is specified. ```ruby title = notification.title ``` ```ruby notification.title # => "USE eager loading in /posts" ``` -------------------------------- ### Configure Stack Trace Include Patterns Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Sets patterns for file paths to include in stack traces, even if they are outside the application. Patterns can be strings, regex, or arrays specifying path and line filters. ```ruby Bullet.stacktrace_includes = ['my_gem', 'my_middleware'] ``` ```ruby patterns = Bullet.stacktrace_includes ``` ```ruby Bullet.stacktrace_includes = [ 'my_special_gem', /^vendor\/my_custom_code/ ] ``` -------------------------------- ### Get Current OS User (Ruby) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves the current operating system user. Caches the result in an instance variable for subsequent calls. ```ruby user = notification.whoami ``` ```ruby notification.whoami # => "boxuser" ``` -------------------------------- ### Rack Middleware Integration Flow Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/05-rack-middleware.md Illustrates the sequence of operations when a Rack middleware processes an HTTP request, from arrival to response. ```text HTTP Request arrives ↓ Rack middleware.call(env) ↓ Bullet.start_request (initialize registries) ↓ @app.call(env) (Rails processes request, Bullet detects issues) ↓ Generate response ↓ Bullet.notification? (check if issues found) ↓ If HTML + notifications: - append_to_html_body(footer_note, notifications, xhr_script) - Update Content-Length ↓ Bullet.perform_out_of_channel_notifications(env) (Slack, Sentry, etc) ↓ Return [status, headers, response_body] ↓ Bullet.end_request (cleanup registries) ↓ Response sent to client ``` -------------------------------- ### Enable HTML Footer Widget Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/06-types-and-configuration.md Set to true to add a details widget to HTML pages that displays Bullet warnings. Requires `inject_into_page?` to return true. ```ruby Bullet.add_footer = true ``` ```ruby Bullet.add_footer = false ``` -------------------------------- ### Test for UnoptimizedQueryError Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Example of how to test for the Bullet::Notification::UnoptimizedQueryError in a test suite. It expects the error to be raised when performing an operation that triggers an unoptimized query. ```ruby expect { Post.all.each { |p| p.comments } }.to raise_error(Bullet::Notification::UnoptimizedQueryError) ``` -------------------------------- ### Configure XMPP Notifications Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/06-types-and-configuration.md Configure `Bullet.xmpp` with a hash of parameters to send XMPP/Jabber notifications. Requires an XMPP server and accounts to know each other. ```ruby Bullet.xmpp = { account: 'bullet_account@jabber.org', password: 'password', receiver: 'your_account@jabber.org', show_online_status: false } ``` -------------------------------- ### Get Footer Notification Info Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Retrieve an array of strings containing short notice messages for all detected notifications, suitable for display in a page footer. ```ruby footer_lines = Bullet.footer_info ``` -------------------------------- ### Check if Key Exists in Registry Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/04-registry-api.md Determines if a given key is present in the registry. Returns true if the key exists, false otherwise. ```ruby if registry.key?(key) puts "Key found" end ``` -------------------------------- ### Get Formatted Associations String for .includes() Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves a formatted string representing the associations in a format suitable for Rails' `.includes()` call. This is a protected method. ```ruby str = notification.send(:associations_str) ``` -------------------------------- ### Bullet.unused_eager_loading_enable= and Bullet.unused_eager_loading_enable? Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Control and check the enablement status for unused eager loading detection. ```APIDOC ## Bullet.unused_eager_loading_enable= ### Description Enable or disable unused eager loading detection. ### Method `Bullet.unused_eager_loading_enable = value` ### Parameters #### Path Parameters (none) #### Query Parameters (none) #### Request Body - **enable** (Boolean) - Required - Whether to detect unused eager loading ### Request Example ```ruby Bullet.unused_eager_loading_enable = true ``` ### Response #### Success Response (200) - **Boolean** - The new enablement status #### Response Example ```ruby true ``` ## Bullet.unused_eager_loading_enable? ### Description Check if unused eager loading detection is enabled. ### Method `Bullet.unused_eager_loading_enable?` ### Parameters #### Path Parameters (none) #### Query Parameters (none) #### Request Body (none) ### Request Example ```ruby if Bullet.unused_eager_loading_enable? puts "Unused eager loading detection is active" end ``` ### Response #### Success Response (200) - **Boolean** - True if unused eager loading detection is enabled, false otherwise #### Response Example ```ruby puts "Unused eager loading is #{Bullet.unused_eager_loading_enable? ? 'enabled' : 'disabled'}" ``` ``` -------------------------------- ### Configure Slack Notifications Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/06-types-and-configuration.md Configure `Bullet.slack` with a hash of parameters to send notifications to Slack. Requires a Slack workspace and an incoming webhook. ```ruby Bullet.slack = { webhook_url: 'https://hooks.slack.com/services/...', channel: '#alerts', username: 'Bullet Notifier' } ``` -------------------------------- ### Get Unused Eager Loading Notification Title Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieve the title of the notification. The format indicates the path where eager loading was detected or a general detection message. ```ruby title = notification.title ``` ```ruby notification.title # => "AVOID eager loading in /posts" ``` -------------------------------- ### Call Method for Rack Middleware Interface Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/05-rack-middleware.md The main entry point for the Rack middleware, responsible for wrapping each HTTP request and response. It handles Bullet's detection and notification injection. ```ruby status, headers, response_body = middleware.call(env) ``` -------------------------------- ### Get Formatted Model and Associations String Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves a formatted string showing the model and its associated associations. This is a protected method, typically accessed via `send`. ```ruby str = notification.send(:klazz_associations_str) ``` -------------------------------- ### Get Formatted Call Stack Messages Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/03-notification-api.md Retrieves a formatted string representing the call stack where an issue occurred. Useful for debugging and understanding the origin of notifications. ```ruby stack = notification.call_stack_messages ``` -------------------------------- ### Register Objects for N+1 Detection - NPlusOneQuery.add_possible_objects Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md Use this method to mark objects loaded from the database as potential candidates for causing N+1 queries. It's called internally when multiple objects are retrieved. ```ruby Bullet::Detector::NPlusOneQuery.add_possible_objects(posts) ``` -------------------------------- ### Object Associations Registry (Base) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/04-registry-api.md Maps each object to all its preloaded associations. Use this to see all associations that have been preloaded for objects. ```ruby { "Post:1" => Set(:comments, :author), "Post:2" => Set(:comments), "Comment:1" => Set(:post) } ``` -------------------------------- ### Enable File Logging Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/01-api-reference-module.md Enable file-based logging to `log/bullet.log`. This setting redirects UniformNotifier's customized_logger to the specified file. ```ruby Bullet.bullet_logger = true ``` ```ruby config.after_initialize do Bullet.enable = true Bullet.bullet_logger = true # Log to log/bullet.log end ``` -------------------------------- ### Bullet Read-Write Accessors Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/06-types-and-configuration.md These accessors allow you to modify Bullet's behavior at runtime. For example, `add_footer` controls whether Bullet adds a footer to your HTML pages. ```ruby Bullet.add_footer Bullet.footer_position Bullet.orm_patches_applied Bullet.skip_http_headers Bullet.always_append_html_body Bullet.skip_user_in_notification ``` -------------------------------- ### Bullet::Rack Constructor Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/05-rack-middleware.md Initializes the Bullet::Rack middleware, wrapping the next application or middleware in the Rack stack. ```APIDOC ## Rack.new ### Description Initialize the middleware. ### Constructor Signature ```ruby middleware = Bullet::Rack.new(app) ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | app | Rack::App | Yes | The next middleware/application in the stack | ### Return Type Bullet::Rack instance ### Example ```ruby # In Rails config: config.middleware.use Bullet::Rack ``` ``` -------------------------------- ### Add/Update Entry (`add`) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/04-registry-api.md Adds or updates an entry in the registry. If the key does not exist, a new Set is created. Values can be added individually or as an Array. ```APIDOC ## `add` Add or update a registry entry. ```ruby registry.add(key, value) ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | key | String, Symbol, Array | Yes | Entry key | | value | Any | Yes | Value to add (can be Array for bulk add) | **Return Type:** Set (the registry value set) **Behavior:** 1. If key doesn't exist: creates new Set 2. If value is Array: adds all items to Set (`+=`) 3. Otherwise: adds single value to Set (`<<`) **Example:** ```ruby registry = Bullet::Registry::Base.new # Add single value registry.add("Post:1", :comments) registry["Post:1"] # => Set(:comments) # Add multiple values as array registry.add("Post:1", [:author, :tags]) registry["Post:1"] # => Set(:comments, :author, :tags) # Add to another key registry.add("Post:2", :comments) ``` ``` -------------------------------- ### Get Association Call Stacks Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/02-detector-api.md Retrieves the registry of stack traces where associations were accessed. This is used for displaying call stacks in notifications to pinpoint N+1 query occurrences. ```ruby stacks = Bullet::Detector::Association.send(:call_stacks) ``` -------------------------------- ### Access Entry (`[]`) Source: https://github.com/flyerhzm/bullet/blob/main/_autodocs/04-registry-api.md Retrieves a registry entry using its key. Returns nil if the key does not exist. ```APIDOC ## `[]` Access a registry entry by key. ```ruby value = registry[key] ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | key | String, Symbol, Array | Yes | The lookup key | **Return Type:** Value (varies: Set, Array, or nil) **Returns:** nil if key doesn't exist **Example:** ```ruby registry = Bullet::Registry::Base.new registry.add("Post:1", :comments) result = registry["Post:1"] # => Set(:comments) ``` ```