bullet 6.1.4 → 6.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +66 -0
- data/CHANGELOG.md +7 -0
- data/README.md +8 -6
- data/lib/bullet.rb +62 -18
- data/lib/bullet/active_record41.rb +1 -0
- data/lib/bullet/active_record42.rb +1 -0
- data/lib/bullet/bullet_xhr.js +1 -0
- data/lib/bullet/detector/base.rb +2 -1
- data/lib/bullet/detector/counter_cache.rb +1 -1
- data/lib/bullet/detector/n_plus_one_query.rb +3 -3
- data/lib/bullet/detector/unused_eager_loading.rb +1 -1
- data/lib/bullet/notification.rb +2 -1
- data/lib/bullet/rack.rb +4 -2
- data/lib/bullet/stack_trace_filter.rb +4 -2
- data/lib/bullet/version.rb +1 -1
- data/perf/benchmark.rb +4 -1
- data/spec/bullet/detector/unused_eager_loading_spec.rb +6 -2
- data/spec/bullet/ext/object_spec.rb +1 -1
- data/spec/bullet/rack_spec.rb +28 -12
- data/spec/bullet_spec.rb +39 -10
- data/spec/integration/active_record/association_spec.rb +17 -8
- data/spec/integration/counter_cache_spec.rb +3 -3
- data/spec/integration/mongoid/association_spec.rb +1 -1
- data/spec/models/deal.rb +5 -0
- data/spec/models/folder.rb +2 -1
- data/spec/models/group.rb +2 -1
- data/spec/models/page.rb +2 -1
- data/spec/models/post.rb +2 -0
- data/spec/models/writer.rb +2 -1
- data/spec/spec_helper.rb +0 -2
- data/spec/support/mongo_seed.rb +1 -0
- data/spec/support/sqlite_seed.rb +12 -0
- data/test.sh +1 -0
- metadata +6 -4
- data/.travis.yml +0 -33
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: b21c2c4ca3caf6c41961a7182a5c8d37f6b89aa78b8dfb7badfb940f11a23191
         | 
| 4 | 
            +
              data.tar.gz: 0f432034f9b4cb2fe6c6572481c797aefcfa69373839fbd0985a7efd2a0bbea9
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d2457987f11f034fa457030cf7161d7508fb26230cf9e88a2571c54a934ad9a1330b48dd3d38eede3033bd7ef6c920e564dcbfa43a15354bf72d14ed799aefe2
         | 
| 7 | 
            +
              data.tar.gz: ed6690fcb3bd778d6d5a27538a0ee78d298c749be1e445c305b9884448ca13f97e5a62c76ac48229a250041bb09ba609d6b154b12cf42a0f3dca66fbb9c83021
         | 
| @@ -0,0 +1,66 @@ | |
| 1 | 
            +
            # This workflow uses actions that are not certified by GitHub.
         | 
| 2 | 
            +
            # They are provided by a third-party and are governed by
         | 
| 3 | 
            +
            # separate terms of service, privacy policy, and support
         | 
| 4 | 
            +
            # documentation.
         | 
| 5 | 
            +
            # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
         | 
| 6 | 
            +
            # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            name: CI
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            on:
         | 
| 11 | 
            +
              push:
         | 
| 12 | 
            +
                branches: [ master ]
         | 
| 13 | 
            +
              pull_request:
         | 
| 14 | 
            +
                branches: [ master ]
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            jobs:
         | 
| 17 | 
            +
              test_rails_4:
         | 
| 18 | 
            +
                runs-on: ubuntu-latest
         | 
| 19 | 
            +
                strategy:
         | 
| 20 | 
            +
                  matrix:
         | 
| 21 | 
            +
                    gemfile: ['Gemfile.rails-4.0', 'Gemfile.rails-4.1', 'Gemfile.rails-4.2']
         | 
| 22 | 
            +
                env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
         | 
| 23 | 
            +
                  BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
         | 
| 24 | 
            +
                steps:
         | 
| 25 | 
            +
                - uses: actions/checkout@v2
         | 
| 26 | 
            +
                - name: Set up Ruby
         | 
| 27 | 
            +
                  uses: ruby/setup-ruby@v1
         | 
| 28 | 
            +
                  with:
         | 
| 29 | 
            +
                    ruby-version: 2.3
         | 
| 30 | 
            +
                    bundler: 1
         | 
| 31 | 
            +
                    bundler-cache: true
         | 
| 32 | 
            +
                - name: Run tests
         | 
| 33 | 
            +
                  run: bundle exec rake
         | 
| 34 | 
            +
              test_rails_5:
         | 
| 35 | 
            +
                runs-on: ubuntu-latest
         | 
| 36 | 
            +
                strategy:
         | 
| 37 | 
            +
                  matrix:
         | 
| 38 | 
            +
                    gemfile: ['Gemfile.rails-5.0', 'Gemfile.rails-5.1', 'Gemfile.rails-5.2']
         | 
| 39 | 
            +
                env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
         | 
| 40 | 
            +
                  BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
         | 
| 41 | 
            +
                steps:
         | 
| 42 | 
            +
                - uses: actions/checkout@v2
         | 
| 43 | 
            +
                - name: Set up Ruby
         | 
| 44 | 
            +
                  uses: ruby/setup-ruby@v1
         | 
| 45 | 
            +
                  with:
         | 
| 46 | 
            +
                    ruby-version: 2.5
         | 
| 47 | 
            +
                    bundler: 1
         | 
| 48 | 
            +
                    bundler-cache: true
         | 
| 49 | 
            +
                - name: Run tests
         | 
| 50 | 
            +
                  run: bundle exec rake
         | 
| 51 | 
            +
              test_rails_6:
         | 
| 52 | 
            +
                runs-on: ubuntu-latest
         | 
| 53 | 
            +
                strategy:
         | 
| 54 | 
            +
                  matrix:
         | 
| 55 | 
            +
                    gemfile: ['Gemfile.rails-6.0', 'Gemfile.rails-6.1']
         | 
| 56 | 
            +
                env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
         | 
| 57 | 
            +
                  BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
         | 
| 58 | 
            +
                steps:
         | 
| 59 | 
            +
                - uses: actions/checkout@v2
         | 
| 60 | 
            +
                - name: Set up Ruby
         | 
| 61 | 
            +
                  uses: ruby/setup-ruby@v1
         | 
| 62 | 
            +
                  with:
         | 
| 63 | 
            +
                    ruby-version: 2.7
         | 
| 64 | 
            +
                    bundler-cache: true
         | 
| 65 | 
            +
                - name: Run tests
         | 
| 66 | 
            +
                  run: bundle exec rake
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,5 +1,12 @@ | |
| 1 1 | 
             
            ## Next Release
         | 
| 2 2 |  | 
| 3 | 
            +
            ## 6.1.5 (08/16/2021)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            * Rename whitelist to safelist
         | 
| 6 | 
            +
            * Fix onload called twice
         | 
| 7 | 
            +
            * Support Rack::Files::Iterator responses
         | 
| 8 | 
            +
            * Ensure HABTM associations are not incorrectly labeled n+1
         | 
| 9 | 
            +
             | 
| 3 10 | 
             
            ## 6.1.4 (02/26/2021)
         | 
| 4 11 |  | 
| 5 12 | 
             
            * Added an option to stop adding HTTP headers to API requests
         | 
    
        data/README.md
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            # Bullet
         | 
| 2 2 |  | 
| 3 | 
            +
            
         | 
| 3 4 | 
             
            [](http://badge.fury.io/rb/bullet)
         | 
| 4 | 
            -
            [](http://travis-ci.org/flyerhzm/bullet)
         | 
| 5 5 | 
             
            [](https://awesomecode.io/repos/flyerhzm/bullet)
         | 
| 6 6 | 
             
            [](http://coderwall.com/flyerhzm)
         | 
| 7 7 |  | 
| @@ -67,6 +67,7 @@ config.after_initialize do | |
| 67 67 | 
             
              Bullet.rails_logger = true
         | 
| 68 68 | 
             
              Bullet.honeybadger = true
         | 
| 69 69 | 
             
              Bullet.bugsnag = true
         | 
| 70 | 
            +
              Bullet.appsignal = true
         | 
| 70 71 | 
             
              Bullet.airbrake = true
         | 
| 71 72 | 
             
              Bullet.rollbar = true
         | 
| 72 73 | 
             
              Bullet.add_footer = true
         | 
| @@ -90,6 +91,7 @@ The code above will enable all of the Bullet notification systems: | |
| 90 91 | 
             
            * `Bullet.honeybadger`: add notifications to Honeybadger
         | 
| 91 92 | 
             
            * `Bullet.bugsnag`: add notifications to bugsnag
         | 
| 92 93 | 
             
            * `Bullet.airbrake`: add notifications to airbrake
         | 
| 94 | 
            +
            * `Bullet.appsignal`: add notifications to AppSignal
         | 
| 93 95 | 
             
            * `Bullet.rollbar`: add notifications to rollbar
         | 
| 94 96 | 
             
            * `Bullet.sentry`: add notifications to sentry
         | 
| 95 97 | 
             
            * `Bullet.add_footer`: adds the details in the bottom left corner of the page. Double click the footer or use close button to hide footer.
         | 
| @@ -119,15 +121,15 @@ Bullet.unused_eager_loading_enable = false | |
| 119 121 | 
             
            Bullet.counter_cache_enable        = false
         | 
| 120 122 | 
             
            ```
         | 
| 121 123 |  | 
| 122 | 
            -
            ##  | 
| 124 | 
            +
            ## Safe list
         | 
| 123 125 |  | 
| 124 126 | 
             
            Sometimes Bullet may notify you of query problems you don't care to fix, or
         | 
| 125 | 
            -
            which come from outside your code. You can  | 
| 127 | 
            +
            which come from outside your code. You can add them to a safe list to ignore them:
         | 
| 126 128 |  | 
| 127 129 | 
             
            ```ruby
         | 
| 128 | 
            -
            Bullet. | 
| 129 | 
            -
            Bullet. | 
| 130 | 
            -
            Bullet. | 
| 130 | 
            +
            Bullet.add_safelist :type => :n_plus_one_query, :class_name => "Post", :association => :comments
         | 
| 131 | 
            +
            Bullet.add_safelist :type => :unused_eager_loading, :class_name => "Post", :association => :comments
         | 
| 132 | 
            +
            Bullet.add_safelist :type => :counter_cache, :class_name => "Country", :association => :cities
         | 
| 131 133 | 
             
            ```
         | 
| 132 134 |  | 
| 133 135 | 
             
            If you want to skip bullet in some specific controller actions, you can
         | 
    
        data/lib/bullet.rb
    CHANGED
    
    | @@ -20,9 +20,6 @@ module Bullet | |
| 20 20 | 
             
              autoload :Registry, 'bullet/registry'
         | 
| 21 21 | 
             
              autoload :NotificationCollector, 'bullet/notification_collector'
         | 
| 22 22 |  | 
| 23 | 
            -
              BULLET_DEBUG = 'BULLET_DEBUG'
         | 
| 24 | 
            -
              TRUE = 'true'
         | 
| 25 | 
            -
             | 
| 26 23 | 
             
              if defined?(Rails::Railtie)
         | 
| 27 24 | 
             
                class BulletRailtie < Rails::Railtie
         | 
| 28 25 | 
             
                  initializer 'bullet.configure_rails_initialization' do |app|
         | 
| @@ -38,10 +35,11 @@ module Bullet | |
| 38 35 | 
             
                            :stacktrace_includes,
         | 
| 39 36 | 
             
                            :stacktrace_excludes,
         | 
| 40 37 | 
             
                            :skip_html_injection
         | 
| 41 | 
            -
                attr_reader : | 
| 38 | 
            +
                attr_reader :safelist
         | 
| 42 39 | 
             
                attr_accessor :add_footer, :orm_patches_applied, :skip_http_headers
         | 
| 43 40 |  | 
| 44 | 
            -
                available_notifiers = | 
| 41 | 
            +
                available_notifiers =
         | 
| 42 | 
            +
                  UniformNotifier::AVAILABLE_NOTIFIERS.select { |notifier| notifier != :raise }.map { |notifier| "#{notifier}=" }
         | 
| 45 43 | 
             
                available_notifiers_options = { to: UniformNotifier }
         | 
| 46 44 | 
             
                delegate(*available_notifiers, **available_notifiers_options)
         | 
| 47 45 |  | 
| @@ -59,7 +57,7 @@ module Bullet | |
| 59 57 | 
             
                  @enable = @n_plus_one_query_enable = @unused_eager_loading_enable = @counter_cache_enable = enable
         | 
| 60 58 |  | 
| 61 59 | 
             
                  if enable?
         | 
| 62 | 
            -
                     | 
| 60 | 
            +
                    reset_safelist
         | 
| 63 61 | 
             
                    unless orm_patches_applied
         | 
| 64 62 | 
             
                      self.orm_patches_applied = true
         | 
| 65 63 | 
             
                      Bullet::Mongoid.enable if mongoid?
         | 
| @@ -72,8 +70,9 @@ module Bullet | |
| 72 70 | 
             
                  !!@enable
         | 
| 73 71 | 
             
                end
         | 
| 74 72 |  | 
| 73 | 
            +
                # Rails.root might be nil if `railties` is a dependency on a project that does not use Rails
         | 
| 75 74 | 
             
                def app_root
         | 
| 76 | 
            -
                  @app_root ||= (defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd).to_s
         | 
| 75 | 
            +
                  @app_root ||= (defined?(::Rails.root) && !::Rails.root.nil? ? Rails.root.to_s : Dir.pwd).to_s
         | 
| 77 76 | 
             
                end
         | 
| 78 77 |  | 
| 79 78 | 
             
                def n_plus_one_query_enable?
         | 
| @@ -96,29 +95,74 @@ module Bullet | |
| 96 95 | 
             
                  @stacktrace_excludes ||= []
         | 
| 97 96 | 
             
                end
         | 
| 98 97 |  | 
| 98 | 
            +
                def add_safelist(options)
         | 
| 99 | 
            +
                  reset_safelist
         | 
| 100 | 
            +
                  @safelist[options[:type]][options[:class_name]] ||= []
         | 
| 101 | 
            +
                  @safelist[options[:type]][options[:class_name]] << options[:association].to_sym
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                def delete_safelist(options)
         | 
| 105 | 
            +
                  reset_safelist
         | 
| 106 | 
            +
                  @safelist[options[:type]][options[:class_name]] ||= []
         | 
| 107 | 
            +
                  @safelist[options[:type]][options[:class_name]].delete(options[:association].to_sym)
         | 
| 108 | 
            +
                  @safelist[options[:type]].delete_if { |_key, val| val.empty? }
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                def get_safelist_associations(type, class_name)
         | 
| 112 | 
            +
                  Array(@safelist[type][class_name])
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                def reset_safelist
         | 
| 116 | 
            +
                  @safelist ||= { n_plus_one_query: {}, unused_eager_loading: {}, counter_cache: {} }
         | 
| 117 | 
            +
                end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                def clear_safelist
         | 
| 120 | 
            +
                  @safelist = nil
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
             | 
| 99 123 | 
             
                def add_whitelist(options)
         | 
| 100 | 
            -
                   | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 124 | 
            +
                  ActiveSupport::Deprecation.warn(<<~WARN.strip
         | 
| 125 | 
            +
                    add_whitelist is deprecated in favor of add_safelist. It will be removed from the next major release.
         | 
| 126 | 
            +
                    WARN
         | 
| 127 | 
            +
                  )
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  add_safelist(options)
         | 
| 103 130 | 
             
                end
         | 
| 104 131 |  | 
| 105 132 | 
             
                def delete_whitelist(options)
         | 
| 106 | 
            -
                   | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
                   | 
| 133 | 
            +
                  ActiveSupport::Deprecation.warn(<<~WARN.strip
         | 
| 134 | 
            +
                    delete_whitelist is deprecated in favor of delete_safelist. It will be removed from the next major release.
         | 
| 135 | 
            +
                    WARN
         | 
| 136 | 
            +
                  )
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                  delete_safelist(options)
         | 
| 110 139 | 
             
                end
         | 
| 111 140 |  | 
| 112 141 | 
             
                def get_whitelist_associations(type, class_name)
         | 
| 113 | 
            -
                   | 
| 142 | 
            +
                  ActiveSupport::Deprecation.warn(<<~WARN.strip
         | 
| 143 | 
            +
                    get_whitelist_associations is deprecated in favor of get_safelist_associations. It will be removed from the next major release.
         | 
| 144 | 
            +
                    WARN
         | 
| 145 | 
            +
                  )
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                  get_safelist_associations(type, class_name)
         | 
| 114 148 | 
             
                end
         | 
| 115 149 |  | 
| 116 150 | 
             
                def reset_whitelist
         | 
| 117 | 
            -
                   | 
| 151 | 
            +
                  ActiveSupport::Deprecation.warn(<<~WARN.strip
         | 
| 152 | 
            +
                    reset_whitelist is deprecated in favor of reset_safelist. It will be removed from the next major release.
         | 
| 153 | 
            +
                    WARN
         | 
| 154 | 
            +
                  )
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                  reset_safelist
         | 
| 118 157 | 
             
                end
         | 
| 119 158 |  | 
| 120 159 | 
             
                def clear_whitelist
         | 
| 121 | 
            -
                   | 
| 160 | 
            +
                  ActiveSupport::Deprecation.warn(<<~WARN.strip
         | 
| 161 | 
            +
                    clear_whitelist is deprecated in favor of clear_safelist. It will be removed from the next major release.
         | 
| 162 | 
            +
                    WARN
         | 
| 163 | 
            +
                  )
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                  clear_safelist
         | 
| 122 166 | 
             
                end
         | 
| 123 167 |  | 
| 124 168 | 
             
                def bullet_logger=(active)
         | 
| @@ -132,7 +176,7 @@ module Bullet | |
| 132 176 | 
             
                end
         | 
| 133 177 |  | 
| 134 178 | 
             
                def debug(title, message)
         | 
| 135 | 
            -
                  puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] ==  | 
| 179 | 
            +
                  puts "[Bullet][#{title}] #{message}" if ENV['BULLET_DEBUG'] == 'true'
         | 
| 136 180 | 
             
                end
         | 
| 137 181 |  | 
| 138 182 | 
             
                def start_request
         | 
| @@ -30,6 +30,7 @@ module Bullet | |
| 30 30 |  | 
| 31 31 | 
             
                  ::ActiveRecord::Relation.class_eval do
         | 
| 32 32 | 
             
                    alias_method :origin_to_a, :to_a
         | 
| 33 | 
            +
             | 
| 33 34 | 
             
                    # if select a collection of objects, then these objects have possible to cause N+1 query.
         | 
| 34 35 | 
             
                    # if select only one object, then the only one object has impossible to cause N+1 query.
         | 
| 35 36 | 
             
                    def to_a
         | 
| @@ -52,6 +52,7 @@ module Bullet | |
| 52 52 |  | 
| 53 53 | 
             
                  ::ActiveRecord::Relation.class_eval do
         | 
| 54 54 | 
             
                    alias_method :origin_to_a, :to_a
         | 
| 55 | 
            +
             | 
| 55 56 | 
             
                    # if select a collection of objects, then these objects have possible to cause N+1 query.
         | 
| 56 57 | 
             
                    # if select only one object, then the only one object has impossible to cause N+1 query.
         | 
| 57 58 | 
             
                    def to_a
         | 
    
        data/lib/bullet/bullet_xhr.js
    CHANGED
    
    
    
        data/lib/bullet/detector/base.rb
    CHANGED
    
    
| @@ -54,7 +54,7 @@ module Bullet | |
| 54 54 | 
             
                    private
         | 
| 55 55 |  | 
| 56 56 | 
             
                    def create_notification(klazz, associations)
         | 
| 57 | 
            -
                      notify_associations = Array(associations) - Bullet. | 
| 57 | 
            +
                      notify_associations = Array(associations) - Bullet.get_safelist_associations(:counter_cache, klazz)
         | 
| 58 58 |  | 
| 59 59 | 
             
                      if notify_associations.present?
         | 
| 60 60 | 
             
                        notice = Bullet::Notification::CounterCache.new klazz, notify_associations
         | 
| @@ -35,6 +35,7 @@ module Bullet | |
| 35 35 |  | 
| 36 36 | 
             
                      objects = Array(object_or_objects)
         | 
| 37 37 | 
             
                      return if objects.map(&:bullet_primary_key_value).compact.empty?
         | 
| 38 | 
            +
                      return if objects.all? { |obj| obj.class.name =~ /^HABTM_/ }
         | 
| 38 39 |  | 
| 39 40 | 
             
                      Bullet.debug(
         | 
| 40 41 | 
             
                        'Detector::NPlusOneQuery#add_possible_objects',
         | 
| @@ -84,8 +85,7 @@ module Bullet | |
| 84 85 | 
             
                        # associations == v comparison order is important here because
         | 
| 85 86 | 
             
                        # v variable might be a squeel node where :== method is redefined,
         | 
| 86 87 | 
             
                        # so it does not compare values at all and return unexpected results
         | 
| 87 | 
            -
                        result =
         | 
| 88 | 
            -
                          v.is_a?(Hash) ? v.key?(associations) : associations == v
         | 
| 88 | 
            +
                        result = v.is_a?(Hash) ? v.key?(associations) : associations == v
         | 
| 89 89 | 
             
                        return true if result
         | 
| 90 90 | 
             
                      end
         | 
| 91 91 |  | 
| @@ -95,7 +95,7 @@ module Bullet | |
| 95 95 | 
             
                    private
         | 
| 96 96 |  | 
| 97 97 | 
             
                    def create_notification(callers, klazz, associations)
         | 
| 98 | 
            -
                      notify_associations = Array(associations) - Bullet. | 
| 98 | 
            +
                      notify_associations = Array(associations) - Bullet.get_safelist_associations(:n_plus_one_query, klazz)
         | 
| 99 99 |  | 
| 100 100 | 
             
                      if notify_associations.present?
         | 
| 101 101 | 
             
                        notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
         | 
| @@ -65,7 +65,7 @@ module Bullet | |
| 65 65 | 
             
                    private
         | 
| 66 66 |  | 
| 67 67 | 
             
                    def create_notification(callers, klazz, associations)
         | 
| 68 | 
            -
                      notify_associations = Array(associations) - Bullet. | 
| 68 | 
            +
                      notify_associations = Array(associations) - Bullet.get_safelist_associations(:unused_eager_loading, klazz)
         | 
| 69 69 |  | 
| 70 70 | 
             
                      if notify_associations.present?
         | 
| 71 71 | 
             
                        notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
         | 
    
        data/lib/bullet/notification.rb
    CHANGED
    
    
    
        data/lib/bullet/rack.rb
    CHANGED
    
    | @@ -22,7 +22,9 @@ module Bullet | |
| 22 22 | 
             
                        response_body = response_body(response)
         | 
| 23 23 | 
             
                        response_body = append_to_html_body(response_body, footer_note) if Bullet.add_footer
         | 
| 24 24 | 
             
                        response_body = append_to_html_body(response_body, Bullet.gather_inline_notifications)
         | 
| 25 | 
            -
                         | 
| 25 | 
            +
                        if Bullet.add_footer && !Bullet.skip_http_headers
         | 
| 26 | 
            +
                          response_body = append_to_html_body(response_body, xhr_script)
         | 
| 27 | 
            +
                        end
         | 
| 26 28 | 
             
                        headers['Content-Length'] = response_body.bytesize.to_s
         | 
| 27 29 | 
             
                      elsif !Bullet.skip_http_headers
         | 
| 28 30 | 
             
                        set_header(headers, 'X-bullet-footer-text', Bullet.footer_info.uniq) if Bullet.add_footer
         | 
| @@ -82,7 +84,7 @@ module Bullet | |
| 82 84 | 
             
                def response_body(response)
         | 
| 83 85 | 
             
                  if response.respond_to?(:body)
         | 
| 84 86 | 
             
                    Array === response.body ? response.body.first : response.body
         | 
| 85 | 
            -
                   | 
| 87 | 
            +
                  elsif response.respond_to?(:first)
         | 
| 86 88 | 
             
                    response.first
         | 
| 87 89 | 
             
                  end
         | 
| 88 90 | 
             
                end
         | 
| @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            +
            require "bundler"
         | 
| 2 3 |  | 
| 3 4 | 
             
            module Bullet
         | 
| 4 5 | 
             
              module StackTraceFilter
         | 
| @@ -11,8 +12,9 @@ module Bullet | |
| 11 12 | 
             
                  select_caller_locations do |location|
         | 
| 12 13 | 
             
                    caller_path = location_as_path(location)
         | 
| 13 14 | 
             
                    caller_path.include?(Bullet.app_root) && !caller_path.include?(vendor_root) &&
         | 
| 14 | 
            -
                      !caller_path.include?(bundler_path) ||
         | 
| 15 | 
            -
                       | 
| 15 | 
            +
                      !caller_path.include?(bundler_path) || Bullet.stacktrace_includes.any? { |include_pattern|
         | 
| 16 | 
            +
                      pattern_matches?(location, include_pattern)
         | 
| 17 | 
            +
                    }
         | 
| 16 18 | 
             
                  end
         | 
| 17 19 | 
             
                end
         | 
| 18 20 |  | 
    
        data/lib/bullet/version.rb
    CHANGED
    
    
    
        data/perf/benchmark.rb
    CHANGED
    
    | @@ -30,7 +30,10 @@ end | |
| 30 30 |  | 
| 31 31 | 
             
            # create database bullet_benchmark;
         | 
| 32 32 | 
             
            ActiveRecord::Base.establish_connection(
         | 
| 33 | 
            -
              adapter: 'mysql2', | 
| 33 | 
            +
              adapter: 'mysql2',
         | 
| 34 | 
            +
              database: 'bullet_benchmark',
         | 
| 35 | 
            +
              server: '/tmp/mysql.socket',
         | 
| 36 | 
            +
              username: 'root'
         | 
| 34 37 | 
             
            )
         | 
| 35 38 |  | 
| 36 39 | 
             
            ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
         | 
| @@ -19,7 +19,9 @@ module Bullet | |
| 19 19 | 
             
                    it 'should get call associations if object and association are both in eager_loadings and call_object_associations' do
         | 
| 20 20 | 
             
                      UnusedEagerLoading.add_eager_loadings([@post], :association)
         | 
| 21 21 | 
             
                      UnusedEagerLoading.add_call_object_associations(@post, :association)
         | 
| 22 | 
            -
                      expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq( | 
| 22 | 
            +
                      expect(UnusedEagerLoading.send(:call_associations, @post.bullet_key, Set.new([:association]))).to eq(
         | 
| 23 | 
            +
                        [:association]
         | 
| 24 | 
            +
                      )
         | 
| 23 25 | 
             
                    end
         | 
| 24 26 |  | 
| 25 27 | 
             
                    it 'should not get call associations if not exist in call_object_associations' do
         | 
| @@ -30,7 +32,9 @@ module Bullet | |
| 30 32 |  | 
| 31 33 | 
             
                  context '.diff_object_associations' do
         | 
| 32 34 | 
             
                    it 'should return associations not exist in call_association' do
         | 
| 33 | 
            -
                      expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq( | 
| 35 | 
            +
                      expect(UnusedEagerLoading.send(:diff_object_associations, @post.bullet_key, Set.new([:association]))).to eq(
         | 
| 36 | 
            +
                        [:association]
         | 
| 37 | 
            +
                      )
         | 
| 34 38 | 
             
                    end
         | 
| 35 39 |  | 
| 36 40 | 
             
                    it 'should return empty if associations exist in call_association' do
         | 
| @@ -10,7 +10,7 @@ describe Object do | |
| 10 10 | 
             
                end
         | 
| 11 11 |  | 
| 12 12 | 
             
                if mongoid?
         | 
| 13 | 
            -
                  it 'should return class with  | 
| 13 | 
            +
                  it 'should return class with namespace and id composition' do
         | 
| 14 14 | 
             
                    post = Mongoid::Post.first
         | 
| 15 15 | 
             
                    expect(post.bullet_key).to eq("Mongoid::Post:#{post.id}")
         | 
| 16 16 | 
             
                  end
         | 
    
        data/spec/bullet/rack_spec.rb
    CHANGED
    
    | @@ -105,9 +105,10 @@ module Bullet | |
| 105 105 |  | 
| 106 106 | 
             
                      it 'should change response body for html safe string if add_footer is true' do
         | 
| 107 107 | 
             
                        expect(Bullet).to receive(:add_footer).exactly(3).times.and_return(true)
         | 
| 108 | 
            -
                        app.response = | 
| 109 | 
            -
                           | 
| 110 | 
            -
             | 
| 108 | 
            +
                        app.response =
         | 
| 109 | 
            +
                          Support::ResponseDouble.new.tap do |response|
         | 
| 110 | 
            +
                            response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
         | 
| 111 | 
            +
                          end
         | 
| 111 112 | 
             
                        _, headers, response = middleware.call('Content-Type' => 'text/html')
         | 
| 112 113 |  | 
| 113 114 | 
             
                        expect(headers['Content-Length']).to eq((73 + middleware.send(:footer_note).length).to_s)
         | 
| @@ -117,7 +118,7 @@ module Bullet | |
| 117 118 | 
             
                      it 'should add the footer-text header for non-html requests when add_footer is true' do
         | 
| 118 119 | 
             
                        allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
         | 
| 119 120 | 
             
                        allow(Bullet).to receive(:footer_info).and_return(['footer text'])
         | 
| 120 | 
            -
                        app.headers = {'Content-Type' => 'application/json'}
         | 
| 121 | 
            +
                        app.headers = { 'Content-Type' => 'application/json' }
         | 
| 121 122 | 
             
                        _, headers, _response = middleware.call({})
         | 
| 122 123 | 
             
                        expect(headers).to include('X-bullet-footer-text' => '["footer text"]')
         | 
| 123 124 | 
             
                      end
         | 
| @@ -131,9 +132,10 @@ module Bullet | |
| 131 132 |  | 
| 132 133 | 
             
                      it 'should change response body for html safe string if console_enabled is true' do
         | 
| 133 134 | 
             
                        expect(Bullet).to receive(:console_enabled?).and_return(true)
         | 
| 134 | 
            -
                        app.response = | 
| 135 | 
            -
                           | 
| 136 | 
            -
             | 
| 135 | 
            +
                        app.response =
         | 
| 136 | 
            +
                          Support::ResponseDouble.new.tap do |response|
         | 
| 137 | 
            +
                            response.body = ActiveSupport::SafeBuffer.new('<html><head></head><body></body></html>')
         | 
| 138 | 
            +
                          end
         | 
| 137 139 | 
             
                        _, headers, response = middleware.call('Content-Type' => 'text/html')
         | 
| 138 140 | 
             
                        expect(headers['Content-Length']).to eq('56')
         | 
| 139 141 | 
             
                        expect(response).to eq(%w[<html><head></head><body><bullet></bullet></body></html>])
         | 
| @@ -142,7 +144,7 @@ module Bullet | |
| 142 144 | 
             
                      it 'should add headers for non-html requests when console_enabled is true' do
         | 
| 143 145 | 
             
                        allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
         | 
| 144 146 | 
             
                        allow(Bullet).to receive(:text_notifications).and_return(['text notifications'])
         | 
| 145 | 
            -
                        app.headers = {'Content-Type' => 'application/json'}
         | 
| 147 | 
            +
                        app.headers = { 'Content-Type' => 'application/json' }
         | 
| 146 148 | 
             
                        _, headers, _response = middleware.call({})
         | 
| 147 149 | 
             
                        expect(headers).to include('X-bullet-console-text' => '["text notifications"]')
         | 
| 148 150 | 
             
                      end
         | 
| @@ -155,13 +157,13 @@ module Bullet | |
| 155 157 | 
             
                      end
         | 
| 156 158 |  | 
| 157 159 | 
             
                      it "shouldn't add headers unnecessarily" do
         | 
| 158 | 
            -
                        app.headers = {'Content-Type' => 'application/json'}
         | 
| 160 | 
            +
                        app.headers = { 'Content-Type' => 'application/json' }
         | 
| 159 161 | 
             
                        _, headers, _response = middleware.call({})
         | 
| 160 162 | 
             
                        expect(headers).not_to include('X-bullet-footer-text')
         | 
| 161 163 | 
             
                        expect(headers).not_to include('X-bullet-console-text')
         | 
| 162 164 | 
             
                      end
         | 
| 163 165 |  | 
| 164 | 
            -
                      context  | 
| 166 | 
            +
                      context 'when skip_http_headers is enabled' do
         | 
| 165 167 | 
             
                        before do
         | 
| 166 168 | 
             
                          allow(Bullet).to receive(:skip_http_headers).and_return(true)
         | 
| 167 169 | 
             
                        end
         | 
| @@ -183,14 +185,14 @@ module Bullet | |
| 183 185 |  | 
| 184 186 | 
             
                        it 'should not add the footer-text header for non-html requests when add_footer is true' do
         | 
| 185 187 | 
             
                          allow(Bullet).to receive(:add_footer).at_least(:once).and_return(true)
         | 
| 186 | 
            -
                          app.headers = {'Content-Type' => 'application/json'}
         | 
| 188 | 
            +
                          app.headers = { 'Content-Type' => 'application/json' }
         | 
| 187 189 | 
             
                          _, headers, _response = middleware.call({})
         | 
| 188 190 | 
             
                          expect(headers).not_to include('X-bullet-footer-text')
         | 
| 189 191 | 
             
                        end
         | 
| 190 192 |  | 
| 191 193 | 
             
                        it 'should not add headers for non-html requests when console_enabled is true' do
         | 
| 192 194 | 
             
                          allow(Bullet).to receive(:console_enabled?).at_least(:once).and_return(true)
         | 
| 193 | 
            -
                          app.headers = {'Content-Type' => 'application/json'}
         | 
| 195 | 
            +
                          app.headers = { 'Content-Type' => 'application/json' }
         | 
| 194 196 | 
             
                          _, headers, _response = middleware.call({})
         | 
| 195 197 | 
             
                          expect(headers).not_to include('X-bullet-console-text')
         | 
| 196 198 | 
             
                        end
         | 
| @@ -259,6 +261,20 @@ module Bullet | |
| 259 261 | 
             
                      expect(middleware.response_body(response)).to eq body_string
         | 
| 260 262 | 
             
                    end
         | 
| 261 263 | 
             
                  end
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                  begin
         | 
| 266 | 
            +
                    require 'rack/files'
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                    context 'when `response` is a Rack::Files::Iterator' do
         | 
| 269 | 
            +
                      let(:response) { instance_double(::Rack::Files::Iterator) }
         | 
| 270 | 
            +
                      before { allow(response).to receive(:is_a?).with(::Rack::Files::Iterator) { true } }
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                      it 'should return nil' do
         | 
| 273 | 
            +
                        expect(middleware.response_body(response)).to be_nil
         | 
| 274 | 
            +
                      end
         | 
| 275 | 
            +
                    end
         | 
| 276 | 
            +
                  rescue LoadError
         | 
| 277 | 
            +
                  end
         | 
| 262 278 | 
             
                end
         | 
| 263 279 | 
             
              end
         | 
| 264 280 | 
             
            end
         | 
    
        data/spec/bullet_spec.rb
    CHANGED
    
    | @@ -74,31 +74,60 @@ describe Bullet, focused: true do | |
| 74 74 | 
             
                end
         | 
| 75 75 | 
             
              end
         | 
| 76 76 |  | 
| 77 | 
            +
              describe '#add_safelist' do
         | 
| 78 | 
            +
                context "for 'special' class names" do
         | 
| 79 | 
            +
                  it 'is added to the safelist successfully' do
         | 
| 80 | 
            +
                    Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
         | 
| 81 | 
            +
                    expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
             | 
| 77 86 | 
             
              describe '#add_whitelist' do
         | 
| 78 87 | 
             
                context "for 'special' class names" do
         | 
| 79 | 
            -
                  it 'is added to the  | 
| 88 | 
            +
                  it 'is added to the safelist successfully' do
         | 
| 80 89 | 
             
                    Bullet.add_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
         | 
| 81 | 
            -
                    expect(Bullet. | 
| 90 | 
            +
                    expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
              end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
              describe '#delete_safelist' do
         | 
| 96 | 
            +
                context "for 'special' class names" do
         | 
| 97 | 
            +
                  it 'is deleted from the safelist successfully' do
         | 
| 98 | 
            +
                    Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
         | 
| 99 | 
            +
                    Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
         | 
| 100 | 
            +
                    expect(Bullet.safelist[:n_plus_one_query]).to eq({})
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                context 'when exists multiple definitions' do
         | 
| 105 | 
            +
                  it 'is deleted from the safelist successfully' do
         | 
| 106 | 
            +
                    Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
         | 
| 107 | 
            +
                    Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
         | 
| 108 | 
            +
                    Bullet.delete_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
         | 
| 109 | 
            +
                    expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
         | 
| 110 | 
            +
                    expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
         | 
| 82 111 | 
             
                  end
         | 
| 83 112 | 
             
                end
         | 
| 84 113 | 
             
              end
         | 
| 85 114 |  | 
| 86 115 | 
             
              describe '#delete_whitelist' do
         | 
| 87 116 | 
             
                context "for 'special' class names" do
         | 
| 88 | 
            -
                  it 'is deleted from the  | 
| 89 | 
            -
                    Bullet. | 
| 117 | 
            +
                  it 'is deleted from the safelist successfully' do
         | 
| 118 | 
            +
                    Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
         | 
| 90 119 | 
             
                    Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
         | 
| 91 | 
            -
                    expect(Bullet. | 
| 120 | 
            +
                    expect(Bullet.safelist[:n_plus_one_query]).to eq({})
         | 
| 92 121 | 
             
                  end
         | 
| 93 122 | 
             
                end
         | 
| 94 123 |  | 
| 95 124 | 
             
                context 'when exists multiple definitions' do
         | 
| 96 | 
            -
                  it 'is deleted from the  | 
| 97 | 
            -
                    Bullet. | 
| 98 | 
            -
                    Bullet. | 
| 125 | 
            +
                  it 'is deleted from the safelist successfully' do
         | 
| 126 | 
            +
                    Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :department)
         | 
| 127 | 
            +
                    Bullet.add_safelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
         | 
| 99 128 | 
             
                    Bullet.delete_whitelist(type: :n_plus_one_query, class_name: 'Klass', association: :team)
         | 
| 100 | 
            -
                    expect(Bullet. | 
| 101 | 
            -
                    expect(Bullet. | 
| 129 | 
            +
                    expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to include :department
         | 
| 130 | 
            +
                    expect(Bullet.get_safelist_associations(:n_plus_one_query, 'Klass')).to_not include :team
         | 
| 102 131 | 
             
                  end
         | 
| 103 132 | 
             
                end
         | 
| 104 133 | 
             
              end
         | 
| @@ -129,7 +129,7 @@ if active_record? | |
| 129 129 | 
             
                    expect(Bullet::Detector::Association).to be_completely_preloading_associations
         | 
| 130 130 | 
             
                  end
         | 
| 131 131 |  | 
| 132 | 
            -
                  it 'should detect unused preload with post =>  | 
| 132 | 
            +
                  it 'should detect unused preload with post => comments, no category => posts' do
         | 
| 133 133 | 
             
                    Category.includes(posts: :comments).each { |category| category.posts.map(&:name) }
         | 
| 134 134 | 
             
                    Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
         | 
| 135 135 | 
             
                    expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Post, :comments)
         | 
| @@ -202,7 +202,7 @@ if active_record? | |
| 202 202 | 
             
                    expect(Bullet::Detector::Association).to be_completely_preloading_associations
         | 
| 203 203 | 
             
                  end
         | 
| 204 204 |  | 
| 205 | 
            -
                  it 'should detect preload with post =>  | 
| 205 | 
            +
                  it 'should detect preload with post => comments' do
         | 
| 206 206 | 
             
                    Post.first.comments.map(&:name)
         | 
| 207 207 | 
             
                    Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
         | 
| 208 208 | 
             
                    expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
         | 
| @@ -401,6 +401,15 @@ if active_record? | |
| 401 401 | 
             
              end
         | 
| 402 402 |  | 
| 403 403 | 
             
              describe Bullet::Detector::Association, 'has_and_belongs_to_many' do
         | 
| 404 | 
            +
                context 'posts <=> deals' do
         | 
| 405 | 
            +
                  it 'should detect preload associations with join tables that have identifier' do
         | 
| 406 | 
            +
                    Post.includes(:deals).each { |post| post.deals.map(&:name) }
         | 
| 407 | 
            +
                    Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
         | 
| 408 | 
            +
                    expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
         | 
| 409 | 
            +
             | 
| 410 | 
            +
                    expect(Bullet::Detector::Association).to be_completely_preloading_associations
         | 
| 411 | 
            +
                  end
         | 
| 412 | 
            +
                end
         | 
| 404 413 | 
             
                context 'students <=> teachers' do
         | 
| 405 414 | 
             
                  it 'should detect non preload associations' do
         | 
| 406 415 | 
             
                    Student.all.each { |student| student.teachers.map(&:name) }
         | 
| @@ -729,9 +738,9 @@ if active_record? | |
| 729 738 | 
             
                  end
         | 
| 730 739 | 
             
                end
         | 
| 731 740 |  | 
| 732 | 
            -
                context ' | 
| 733 | 
            -
                  before { Bullet. | 
| 734 | 
            -
                  after { Bullet. | 
| 741 | 
            +
                context 'add n plus one query to safelist' do
         | 
| 742 | 
            +
                  before { Bullet.add_safelist type: :n_plus_one_query, class_name: 'Post', association: :comments }
         | 
| 743 | 
            +
                  after { Bullet.clear_safelist }
         | 
| 735 744 |  | 
| 736 745 | 
             
                  it 'should not detect n plus one query' do
         | 
| 737 746 | 
             
                    Post.all.each { |post| post.comments.map(&:name) }
         | 
| @@ -750,9 +759,9 @@ if active_record? | |
| 750 759 | 
             
                  end
         | 
| 751 760 | 
             
                end
         | 
| 752 761 |  | 
| 753 | 
            -
                context ' | 
| 754 | 
            -
                  before { Bullet. | 
| 755 | 
            -
                  after { Bullet. | 
| 762 | 
            +
                context 'add unused eager loading to safelist' do
         | 
| 763 | 
            +
                  before { Bullet.add_safelist type: :unused_eager_loading, class_name: 'Post', association: :comments }
         | 
| 764 | 
            +
                  after { Bullet.clear_safelist }
         | 
| 756 765 |  | 
| 757 766 | 
             
                  it 'should not detect unused eager loading' do
         | 
| 758 767 | 
             
                    Post.includes(:comments).map(&:name)
         | 
| @@ -55,9 +55,9 @@ if !mongoid? && active_record? | |
| 55 55 | 
             
                  end
         | 
| 56 56 | 
             
                end
         | 
| 57 57 |  | 
| 58 | 
            -
                context ' | 
| 59 | 
            -
                  before { Bullet. | 
| 60 | 
            -
                  after { Bullet. | 
| 58 | 
            +
                context 'safelist' do
         | 
| 59 | 
            +
                  before { Bullet.add_safelist type: :counter_cache, class_name: 'Country', association: :cities }
         | 
| 60 | 
            +
                  after { Bullet.clear_safelist }
         | 
| 61 61 |  | 
| 62 62 | 
             
                  it 'should not detect counter cache' do
         | 
| 63 63 | 
             
                    Country.all.each { |country| country.cities.size }
         | 
| @@ -118,7 +118,7 @@ if mongoid? | |
| 118 118 | 
             
                      expect(Bullet::Detector::Association).to be_completely_preloading_associations
         | 
| 119 119 | 
             
                    end
         | 
| 120 120 |  | 
| 121 | 
            -
                    it 'should detect preload with post =>  | 
| 121 | 
            +
                    it 'should detect preload with post => comments' do
         | 
| 122 122 | 
             
                      Mongoid::Post.first.comments.map(&:name)
         | 
| 123 123 | 
             
                      Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
         | 
| 124 124 | 
             
                      expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
         | 
    
        data/spec/models/deal.rb
    ADDED
    
    
    
        data/spec/models/folder.rb
    CHANGED
    
    
    
        data/spec/models/group.rb
    CHANGED
    
    
    
        data/spec/models/page.rb
    CHANGED
    
    
    
        data/spec/models/post.rb
    CHANGED
    
    | @@ -4,6 +4,7 @@ class Post < ActiveRecord::Base | |
| 4 4 | 
             
              belongs_to :category, inverse_of: :posts
         | 
| 5 5 | 
             
              belongs_to :writer
         | 
| 6 6 | 
             
              has_many :comments, inverse_of: :post
         | 
| 7 | 
            +
              has_and_belongs_to_many :deals
         | 
| 7 8 |  | 
| 8 9 | 
             
              validates :category, presence: true
         | 
| 9 10 |  | 
| @@ -21,6 +22,7 @@ class Post < ActiveRecord::Base | |
| 21 22 | 
             
                next unless trigger_after_save
         | 
| 22 23 |  | 
| 23 24 | 
             
                temp_comment = Comment.new(post: self)
         | 
| 25 | 
            +
             | 
| 24 26 | 
             
                # this triggers self to be "possible", even though it's
         | 
| 25 27 | 
             
                # not saved yet
         | 
| 26 28 | 
             
                temp_comment.post
         | 
    
        data/spec/models/writer.rb
    CHANGED
    
    
    
        data/spec/spec_helper.rb
    CHANGED
    
    
    
        data/spec/support/mongo_seed.rb
    CHANGED
    
    | @@ -45,6 +45,7 @@ module Support | |
| 45 45 | 
             
                    Mongoid.configure do |config|
         | 
| 46 46 | 
             
                      config.load_configuration(clients: { default: { database: 'bullet', hosts: %w[localhost:27017] } })
         | 
| 47 47 | 
             
                    end
         | 
| 48 | 
            +
             | 
| 48 49 | 
             
                    # Increase the level from DEBUG in order to avoid excessive logging to the screen
         | 
| 49 50 | 
             
                    Mongo::Logger.logger.level = Logger::WARN
         | 
| 50 51 | 
             
                  end
         | 
    
        data/spec/support/sqlite_seed.rb
    CHANGED
    
    | @@ -21,6 +21,13 @@ module Support | |
| 21 21 | 
             
                  post2 = category2.posts.create(name: 'second', writer: writer2)
         | 
| 22 22 | 
             
                  post3 = category2.posts.create(name: 'third', writer: writer2)
         | 
| 23 23 |  | 
| 24 | 
            +
                  deal1 = Deal.new(name: 'Deal 1')
         | 
| 25 | 
            +
                  deal1.posts << post1
         | 
| 26 | 
            +
                  deal1.posts << post2
         | 
| 27 | 
            +
                  deal2 = Deal.new(name: 'Deal 2')
         | 
| 28 | 
            +
                  post1.deals << deal1
         | 
| 29 | 
            +
                  post1.deals << deal2
         | 
| 30 | 
            +
             | 
| 24 31 | 
             
                  comment1 = post1.comments.create(name: 'first', author: writer1)
         | 
| 25 32 | 
             
                  comment2 = post1.comments.create(name: 'first2', author: writer1)
         | 
| 26 33 | 
             
                  comment3 = post1.comments.create(name: 'first3', author: writer1)
         | 
| @@ -156,6 +163,11 @@ module Support | |
| 156 163 | 
             
                      t.column :hotel_id, :integer
         | 
| 157 164 | 
             
                    end
         | 
| 158 165 |  | 
| 166 | 
            +
                    create_table :deals_posts do |t|
         | 
| 167 | 
            +
                      t.column :deal_id, :integer
         | 
| 168 | 
            +
                      t.column :post_id, :integer
         | 
| 169 | 
            +
                    end
         | 
| 170 | 
            +
             | 
| 159 171 | 
             
                    create_table :documents do |t|
         | 
| 160 172 | 
             
                      t.string :name
         | 
| 161 173 | 
             
                      t.string :type
         | 
    
        data/test.sh
    CHANGED
    
    | @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            #bundle update rails && bundle exec rspec spec
         | 
| 2 2 | 
             
            #BUNDLE_GEMFILE=Gemfile.mongoid bundle update mongoid && BUNDLE_GEMFILE=Gemfile.mongoid bundle exec rspec spec
         | 
| 3 | 
            +
            BUNDLE_GEMFILE=Gemfile.rails-6.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-6.1 bundle exec rspec spec
         | 
| 3 4 | 
             
            BUNDLE_GEMFILE=Gemfile.rails-6.0 bundle && BUNDLE_GEMFILE=Gemfile.rails-6.0 bundle exec rspec spec
         | 
| 4 5 | 
             
            BUNDLE_GEMFILE=Gemfile.rails-5.2 bundle && BUNDLE_GEMFILE=Gemfile.rails-5.2 bundle exec rspec spec
         | 
| 5 6 | 
             
            BUNDLE_GEMFILE=Gemfile.rails-5.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-5.1 bundle exec rspec spec
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: bullet
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 6.1. | 
| 4 | 
            +
              version: 6.1.5
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Richard Huang
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2021- | 
| 11 | 
            +
            date: 2021-08-16 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: activesupport
         | 
| @@ -45,9 +45,9 @@ executables: [] | |
| 45 45 | 
             
            extensions: []
         | 
| 46 46 | 
             
            extra_rdoc_files: []
         | 
| 47 47 | 
             
            files:
         | 
| 48 | 
            +
            - ".github/workflows/main.yml"
         | 
| 48 49 | 
             
            - ".gitignore"
         | 
| 49 50 | 
             
            - ".rspec"
         | 
| 50 | 
            -
            - ".travis.yml"
         | 
| 51 51 | 
             
            - CHANGELOG.md
         | 
| 52 52 | 
             
            - Gemfile
         | 
| 53 53 | 
             
            - Gemfile.mongoid
         | 
| @@ -138,6 +138,7 @@ files: | |
| 138 138 | 
             
            - spec/models/comment.rb
         | 
| 139 139 | 
             
            - spec/models/company.rb
         | 
| 140 140 | 
             
            - spec/models/country.rb
         | 
| 141 | 
            +
            - spec/models/deal.rb
         | 
| 141 142 | 
             
            - spec/models/document.rb
         | 
| 142 143 | 
             
            - spec/models/entry.rb
         | 
| 143 144 | 
             
            - spec/models/firm.rb
         | 
| @@ -191,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 191 192 | 
             
                - !ruby/object:Gem::Version
         | 
| 192 193 | 
             
                  version: 1.3.6
         | 
| 193 194 | 
             
            requirements: []
         | 
| 194 | 
            -
            rubygems_version: 3. | 
| 195 | 
            +
            rubygems_version: 3.2.22
         | 
| 195 196 | 
             
            signing_key:
         | 
| 196 197 | 
             
            specification_version: 4
         | 
| 197 198 | 
             
            summary: help to kill N+1 queries and unused eager loading.
         | 
| @@ -226,6 +227,7 @@ test_files: | |
| 226 227 | 
             
            - spec/models/comment.rb
         | 
| 227 228 | 
             
            - spec/models/company.rb
         | 
| 228 229 | 
             
            - spec/models/country.rb
         | 
| 230 | 
            +
            - spec/models/deal.rb
         | 
| 229 231 | 
             
            - spec/models/document.rb
         | 
| 230 232 | 
             
            - spec/models/entry.rb
         | 
| 231 233 | 
             
            - spec/models/firm.rb
         | 
    
        data/.travis.yml
    DELETED
    
    | @@ -1,33 +0,0 @@ | |
| 1 | 
            -
            language: ruby
         | 
| 2 | 
            -
            rvm:
         | 
| 3 | 
            -
              - 2.3.0
         | 
| 4 | 
            -
              - 2.6.0
         | 
| 5 | 
            -
            gemfile:
         | 
| 6 | 
            -
              - Gemfile.rails-6.0
         | 
| 7 | 
            -
              - Gemfile.rails-5.2
         | 
| 8 | 
            -
              - Gemfile.rails-5.1
         | 
| 9 | 
            -
              - Gemfile.rails-5.0
         | 
| 10 | 
            -
              - Gemfile.rails-4.2
         | 
| 11 | 
            -
              - Gemfile.rails-4.1
         | 
| 12 | 
            -
              - Gemfile.rails-4.0
         | 
| 13 | 
            -
            matrix:
         | 
| 14 | 
            -
              exclude:
         | 
| 15 | 
            -
                - rvm: 2.3.0
         | 
| 16 | 
            -
                  gemfile: Gemfile.rails-6.0
         | 
| 17 | 
            -
                - rvm: 2.6.0
         | 
| 18 | 
            -
                  gemfile: Gemfile.rails-5.2
         | 
| 19 | 
            -
                - rvm: 2.6.0
         | 
| 20 | 
            -
                  gemfile: Gemfile.rails-5.1
         | 
| 21 | 
            -
                - rvm: 2.6.0
         | 
| 22 | 
            -
                  gemfile: Gemfile.rails-5.0
         | 
| 23 | 
            -
                - rvm: 2.6.0
         | 
| 24 | 
            -
                  gemfile: Gemfile.rails-4.2
         | 
| 25 | 
            -
                - rvm: 2.6.0
         | 
| 26 | 
            -
                  gemfile: Gemfile.rails-4.1
         | 
| 27 | 
            -
                - rvm: 2.6.0
         | 
| 28 | 
            -
                  gemfile: Gemfile.rails-4.0
         | 
| 29 | 
            -
            env:
         | 
| 30 | 
            -
              - DB=sqlite
         | 
| 31 | 
            -
            before_install:
         | 
| 32 | 
            -
              - "find /home/travis/.rvm/rubies -wholename '*default/bundler-*.gemspec' -delete"
         | 
| 33 | 
            -
              - gem install bundler -v '< 2'
         |