Skip to content

Commit

Permalink
Merge branch 'master' into rhymes/rename-whitelist-to-safelist
Browse files Browse the repository at this point in the history
  • Loading branch information
flyerhzm committed Aug 16, 2021
2 parents d3854f9 + e44e375 commit 6d5edca
Show file tree
Hide file tree
Showing 43 changed files with 349 additions and 79 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -28,6 +28,7 @@ matrix:
gemfile: Gemfile.rails-4.0
env:
- DB=sqlite
cache: bundler
before_install:
- "find /home/travis/.rvm/rubies -wholename '*default/bundler-*.gemspec' -delete"
- gem install bundler -v '< 2'
18 changes: 18 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,23 @@
## Next Release

## 6.1.4 (02/26/2021)

* Added an option to stop adding HTTP headers to API requests

## 6.1.3 (01/21/2021)

* Consider ThroughAssociation at SingularAssociation like CollectionAssociation
* Add xhr_script only when add_footer is enabled

## 6.1.2 (12/12/2020)

* Revert "Make whitelist thread safe"

## 6.1.1 (12/12/2020)

* Add support Rails 6.1
* Make whitelist thread safe

## 6.1.0 (12/28/2019)

* Add skip_html_injection flag
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.rails-6.0
Expand Up @@ -2,7 +2,7 @@ source "https://rubygems.org"

gemspec

gem 'rails', '6.0.0'
gem 'rails', '~> 6.0.0'
gem 'sqlite3'
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
gem 'activerecord-import'
Expand Down
15 changes: 15 additions & 0 deletions Gemfile.rails-6.1
@@ -0,0 +1,15 @@
source "https://rubygems.org"

gemspec

gem 'rails', '~> 6.1.0'
gem 'sqlite3'
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
gem 'activerecord-import'

gem "rspec"

platforms :rbx do
gem 'rubysl', '~> 2.0'
gem 'rubinius-developer_tools'
end
12 changes: 11 additions & 1 deletion README.md
Expand Up @@ -37,6 +37,13 @@ or add it into a Gemfile (Bundler):
gem 'bullet', group: 'development'
```

enable the Bullet gem with generate command

```ruby
bundle exec rails g bullet:install
```
The generate command will auto generate the default configuration and may ask to include in the test environment as well. See below for custom configuration.

**Note**: make sure `bullet` gem is added after activerecord (rails) and
mongoid.

Expand All @@ -60,6 +67,7 @@ config.after_initialize do
Bullet.rails_logger = true
Bullet.honeybadger = true
Bullet.bugsnag = true
Bullet.appsignal = true
Bullet.airbrake = true
Bullet.rollbar = true
Bullet.add_footer = true
Expand All @@ -83,10 +91,12 @@ The code above will enable all of the Bullet notification systems:
* `Bullet.honeybadger`: add notifications to Honeybadger
* `Bullet.bugsnag`: add notifications to bugsnag
* `Bullet.airbrake`: add notifications to airbrake
* `Bullet.appsignal`: add notifications to AppSignal
* `Bullet.rollbar`: add notifications to rollbar
* `Bullet.sentry`: add notifications to sentry
* `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.
* `Bullet.skip_html_injection`: prevents Bullet from injecting XHR into the returned HTML. This must be false for receiving alerts or console logging.
* `Bullet.skip_html_injection`: prevents Bullet from injecting code into the returned HTML. This must be false for receiving alerts, showing the footer or console logging.
* `Bullet.skip_http_headers`: don't add headers to API requests, and remove the javascript that relies on them. Note that this prevents bullet from logging warnings to the browser console or updating the footer.
* `Bullet.stacktrace_includes`: include paths with any of these substrings in the stack trace, even if they are not in your main app
* `Bullet.stacktrace_excludes`: ignore paths with any of these substrings in the stack trace, even if they are not in your main app.
Each item can be a string (match substring), a regex, or an array where the first item is a path to match, and the second
Expand Down
38 changes: 20 additions & 18 deletions lib/bullet.rb
Expand Up @@ -20,9 +20,6 @@ module Bullet
autoload :Registry, 'bullet/registry'
autoload :NotificationCollector, 'bullet/notification_collector'

BULLET_DEBUG = 'BULLET_DEBUG'
TRUE = 'true'

if defined?(Rails::Railtie)
class BulletRailtie < Rails::Railtie
initializer 'bullet.configure_rails_initialization' do |app|
Expand All @@ -38,9 +35,11 @@ class << self
:stacktrace_includes,
:stacktrace_excludes,
:skip_html_injection
attr_accessor :add_footer, :orm_patches_applied
attr_reader :safelist
attr_accessor :add_footer, :orm_patches_applied, :skip_http_headers

available_notifiers = UniformNotifier::AVAILABLE_NOTIFIERS.map { |notifier| "#{notifier}=" }
available_notifiers =
UniformNotifier::AVAILABLE_NOTIFIERS.select { |notifier| notifier != :raise }.map { |notifier| "#{notifier}=" }
available_notifiers_options = { to: UniformNotifier }
delegate(*available_notifiers, **available_notifiers_options)

Expand Down Expand Up @@ -71,8 +70,9 @@ def enable?
!!@enable
end

# Rails.root might be nil if `railties` is a dependency on a project that does not use Rails
def app_root
(defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd).to_s
@app_root ||= (defined?(::Rails.root) && !::Rails.root.nil? ? Rails.root.to_s : Dir.pwd).to_s
end

def n_plus_one_query_enable?
Expand All @@ -88,36 +88,36 @@ def counter_cache_enable?
end

def stacktrace_includes
@stacktrace_includes || []
@stacktrace_includes ||= []
end

def stacktrace_excludes
@stacktrace_excludes || []
@stacktrace_excludes ||= []
end

def add_safelist(options)
reset_safelist
Thread.current[:safelist][options[:type]][options[:class_name]] ||= []
Thread.current[:safelist][options[:type]][options[:class_name]] << options[:association].to_sym
@safelist[options[:type]][options[:class_name]] ||= []
@safelist[options[:type]][options[:class_name]] << options[:association].to_sym
end

def delete_safelist(options)
reset_safelist
Thread.current[:safelist][options[:type]][options[:class_name]] ||= []
Thread.current[:safelist][options[:type]][options[:class_name]].delete(options[:association].to_sym)
Thread.current[:safelist][options[:type]].delete_if { |_key, val| val.empty? }
@safelist[options[:type]][options[:class_name]] ||= []
@safelist[options[:type]][options[:class_name]].delete(options[:association].to_sym)
@safelist[options[:type]].delete_if { |_key, val| val.empty? }
end

def get_safelist_associations(type, class_name)
Array(Thread.current[:safelist][type][class_name])
Array(@safelist[type][class_name])
end

def reset_safelist
Thread.current[:safelist] ||= { n_plus_one_query: {}, unused_eager_loading: {}, counter_cache: {} }
@safelist ||= { n_plus_one_query: {}, unused_eager_loading: {}, counter_cache: {} }
end

def clear_safelist
Thread.current[:safelist] = nil
@safelist = nil
end

def add_whitelist(options)
Expand Down Expand Up @@ -176,7 +176,7 @@ def bullet_logger=(active)
end

def debug(title, message)
puts "[Bullet][#{title}] #{message}" if ENV[BULLET_DEBUG] == TRUE
puts "[Bullet][#{title}] #{message}" if ENV['BULLET_DEBUG'] == 'true'
end

def start_request
Expand Down Expand Up @@ -285,7 +285,9 @@ def console_enabled?
end

def inject_into_page?
!@skip_html_injection && (console_enabled? || add_footer)
return false if defined?(@skip_html_injection) && @skip_html_injection

console_enabled? || add_footer
end

private
Expand Down
1 change: 1 addition & 0 deletions lib/bullet/active_record41.rb
Expand Up @@ -30,6 +30,7 @@ def find_by_sql(sql, binds = [])

::ActiveRecord::Relation.class_eval do
alias_method :origin_to_a, :to_a

# if select a collection of objects, then these objects have possible to cause N+1 query.
# if select only one object, then the only one object has impossible to cause N+1 query.
def to_a
Expand Down
1 change: 1 addition & 0 deletions lib/bullet/active_record42.rb
Expand Up @@ -52,6 +52,7 @@ def _create_record_with_bullet(*args)

::ActiveRecord::Relation.class_eval do
alias_method :origin_to_a, :to_a

# if select a collection of objects, then these objects have possible to cause N+1 query.
# if select only one object, then the only one object has impossible to cause N+1 query.
def to_a
Expand Down
11 changes: 11 additions & 0 deletions lib/bullet/active_record52.rb
Expand Up @@ -202,6 +202,17 @@ def target

if Bullet.start?
if owner.class.name !~ /^HABTM_/ && !@inversed
if is_a? ::ActiveRecord::Associations::ThroughAssociation
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
association = owner.association reflection.through_reflection.name
Array(association.target).each do |through_record|
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
end

if reflection.through_reflection != through_reflection
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
end
end
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)

if Bullet::Detector::NPlusOneQuery.impossible?(owner)
Expand Down
11 changes: 11 additions & 0 deletions lib/bullet/active_record60.rb
Expand Up @@ -229,6 +229,17 @@ def target

if Bullet.start?
if owner.class.name !~ /^HABTM_/ && !@inversed
if is_a? ::ActiveRecord::Associations::ThroughAssociation
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
association = owner.association(reflection.through_reflection.name)
Array(association.target).each do |through_record|
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
end

if reflection.through_reflection != through_reflection
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
end
end
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)

if Bullet::Detector::NPlusOneQuery.impossible?(owner)
Expand Down
11 changes: 11 additions & 0 deletions lib/bullet/active_record61.rb
Expand Up @@ -229,6 +229,17 @@ def target

if Bullet.start?
if owner.class.name !~ /^HABTM_/ && !@inversed
if is_a? ::ActiveRecord::Associations::ThroughAssociation
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
association = owner.association(reflection.through_reflection.name)
Array(association.target).each do |through_record|
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
end

if reflection.through_reflection != through_reflection
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
end
end
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)

if Bullet::Detector::NPlusOneQuery.impossible?(owner)
Expand Down
5 changes: 3 additions & 2 deletions lib/bullet/bullet_xhr.js
Expand Up @@ -20,6 +20,7 @@
if (this.onload) {
this._storedOnload = this.onload;
}
this.onload = null
this.addEventListener("load", bulletXHROnload);
return Reflect.apply(oldSend, this, arguments);
}
Expand All @@ -30,7 +31,7 @@
) {
var bulletFooterText = this.getResponseHeader("X-bullet-footer-text");
if (bulletFooterText) {
setTimeout(() => {
setTimeout(function() {
var oldHtml = document.querySelector("#bullet-footer").innerHTML.split("<br>");
var header = oldHtml[0];
oldHtml = oldHtml.slice(1, oldHtml.length);
Expand All @@ -41,7 +42,7 @@
}
var bulletConsoleText = this.getResponseHeader("X-bullet-console-text");
if (bulletConsoleText && typeof console !== "undefined" && console.log) {
setTimeout(() => {
setTimeout(function() {
JSON.parse(bulletConsoleText).forEach((message) => {
if (console.groupCollapsed && console.groupEnd) {
console.groupCollapsed("Uniform Notifier");
Expand Down
3 changes: 2 additions & 1 deletion lib/bullet/detector/base.rb
Expand Up @@ -2,6 +2,7 @@

module Bullet
module Detector
class Base; end
class Base
end
end
end
4 changes: 2 additions & 2 deletions lib/bullet/detector/n_plus_one_query.rb
Expand Up @@ -35,6 +35,7 @@ def add_possible_objects(object_or_objects)

objects = Array(object_or_objects)
return if objects.map(&:bullet_primary_key_value).compact.empty?
return if objects.all? { |obj| obj.class.name =~ /^HABTM_/ }

Bullet.debug(
'Detector::NPlusOneQuery#add_possible_objects',
Expand Down Expand Up @@ -84,8 +85,7 @@ def association?(object, associations)
# associations == v comparison order is important here because
# v variable might be a squeel node where :== method is redefined,
# so it does not compare values at all and return unexpected results
result =
v.is_a?(Hash) ? v.key?(associations) : associations == v
result = v.is_a?(Hash) ? v.key?(associations) : associations == v
return true if result
end

Expand Down
2 changes: 1 addition & 1 deletion lib/bullet/mongoid4x.rb
Expand Up @@ -23,7 +23,7 @@ def last
end

def each(&block)
return to_enum unless block_given?
return to_enum unless block

records = []
origin_each { |record| records << record }
Expand Down
2 changes: 1 addition & 1 deletion lib/bullet/mongoid5x.rb
Expand Up @@ -23,7 +23,7 @@ def last
end

def each(&block)
return to_enum unless block_given?
return to_enum unless block

records = []
origin_each { |record| records << record }
Expand Down
2 changes: 1 addition & 1 deletion lib/bullet/mongoid6x.rb
Expand Up @@ -23,7 +23,7 @@ def last(opt = {})
end

def each(&block)
return to_enum unless block_given?
return to_enum unless block

records = []
origin_each { |record| records << record }
Expand Down
2 changes: 1 addition & 1 deletion lib/bullet/mongoid7x.rb
Expand Up @@ -23,7 +23,7 @@ def last(opts = {})
end

def each(&block)
return to_enum unless block_given?
return to_enum unless block

records = []
origin_each { |record| records << record }
Expand Down
3 changes: 2 additions & 1 deletion lib/bullet/notification.rb
Expand Up @@ -7,6 +7,7 @@ module Notification
autoload :NPlusOneQuery, 'bullet/notification/n_plus_one_query'
autoload :CounterCache, 'bullet/notification/counter_cache'

class UnoptimizedQueryError < StandardError; end
class UnoptimizedQueryError < StandardError
end
end
end

0 comments on commit 6d5edca

Please sign in to comment.