Skip to content

Commit

Permalink
Update and merge branch 'flyerhzm/master' #osl
Browse files Browse the repository at this point in the history
* flyerhzm/master:
  Auto corrected by following Style/DeprecatedHashMethods
  Reduce object count
  start? should check enable?
  Rename to mongoid 5.0.0-pre.
  Use Travis' container-based infrastructure.
  Backport the Query-after-Create fix to AR4.1 and AR4.0.
  Do not report association queries immediately after object creation to require a preload.
  README: fix typo
  add note in README that bullet gem must be added after AR and mongoid flyerhzm#222
  support mongoid 5.0.0
  Removed circular argument reference error
  Bumping version to 4.14.8
  instruct how to skip bullet in controller action
  compatible with composite_primary_keys gem flyerhzm#214
  Bumping version to 4.14.7
  • Loading branch information
hannahb0t committed Sep 18, 2015
2 parents 77f2eb2 + e60794c commit 1e20e0d
Show file tree
Hide file tree
Showing 21 changed files with 242 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
@@ -1,3 +1,4 @@
sudo: false
language: ruby
rvm:
- 2.0
Expand All @@ -10,6 +11,7 @@ gemfile:
- Gemfile.rails-3.2
- Gemfile.rails-3.1
- Gemfile.rails-3.0
- Gemfile.mongoid-5.0
- Gemfile.mongoid-4.0
- Gemfile.mongoid-3.1
- Gemfile.mongoid-3.0
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,14 @@
# Next Release

## 4.14.8

* compatible with `composite_primary_keys` gem

## 4.14.7

* Fix AR 4.2 SingularAssociation#reader result can be nil
* `perform_out_of_channel_notifications` should always be triggered

## 4.14.6

* Fix false positive with `belongs_to` -> `belongs_to` for active\_record 4.2
Expand Down
17 changes: 17 additions & 0 deletions Gemfile.mongoid-5.0
@@ -0,0 +1,17 @@
source "https://rubygems.org"

gemspec

gem 'rails', '~> 4.0.0'
gem 'sqlite3', platforms: [:ruby]
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
gem 'mongoid', '~> 5.0.0.beta', github: 'mongoid'

gem "rspec"

gem 'coveralls', require: false

platforms :rbx do
gem 'rubysl', '~> 2.0'
gem 'rubinius-developer_tools'
end
19 changes: 19 additions & 0 deletions README.md
Expand Up @@ -36,6 +36,9 @@ or add it into a Gemfile (Bundler):
gem "bullet", :group => "development"
```

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

## Configuration

Bullet won't do ANYTHING unless you tell it to explicitly. Append to
Expand Down Expand Up @@ -109,6 +112,22 @@ Bullet.add_whitelist :type => :unused_eager_loading, :class_name => "Post", :ass
Bullet.add_whitelist :type => :counter_cache, :class_name => "Country", :association => :cities
```

If you want to skip bullet in some specific controller actions, you can
do like

```ruby
class ApplicationController < ActionController::Base
around_action :skip_bullet

def skip_bullet
Bullet.enable = false
yield
ensure
Bullet.enable = true
end
end
```

## Log

The Bullet log `log/bullet.log` will look something like this:
Expand Down
7 changes: 5 additions & 2 deletions lib/bullet.rb
Expand Up @@ -16,6 +16,9 @@ module Bullet
autoload :Registry, 'bullet/registry'
autoload :NotificationCollector, 'bullet/notification_collector'

BULLET_DEBUG = 'BULLET_DEBUG'.freeze
TRUE = 'true'.freeze

if defined? Rails::Railtie
class BulletRailtie < Rails::Railtie
initializer "bullet.configure_rails_initialization" do |app|
Expand Down Expand Up @@ -98,7 +101,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 @@ -132,7 +135,7 @@ def end_request
end

def start?
Thread.current[:bullet_start]
enable? && Thread.current[:bullet_start]
end

def notification_collector
Expand Down
4 changes: 3 additions & 1 deletion lib/bullet/active_record3.rb
@@ -1,5 +1,7 @@
module Bullet
module ActiveRecord
LOAD_TARGET = 'load_target'.freeze

def self.enable
require 'active_record'
::ActiveRecord::Relation.class_eval do
Expand Down Expand Up @@ -138,7 +140,7 @@ def load_target
# avoid stack level too deep
result = origin_load_target
if Bullet.start?
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.any? { |c| c.include?("load_target") }
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless caller.any? { |c| c.include?(LOAD_TARGET) }
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
end
result
Expand Down
2 changes: 1 addition & 1 deletion lib/bullet/active_record3x.rb
Expand Up @@ -143,7 +143,7 @@ def set_inverse_instance(record)
::ActiveRecord::Associations::HasManyAssociation.class_eval do
alias_method :origin_has_cached_counter?, :has_cached_counter?

def has_cached_counter?(reflection = reflection)
def has_cached_counter?(reflection = reflection())
result = origin_has_cached_counter?(reflection)
if Bullet.start?
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) unless result
Expand Down
10 changes: 10 additions & 0 deletions lib/bullet/active_record4.rb
Expand Up @@ -21,6 +21,16 @@ def to_a
end
end

::ActiveRecord::Persistence.class_eval do
def save_with_bullet(*args, &proc)
was_new_record = new_record?
save_without_bullet(*args, &proc).tap do |result|
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
end
end
alias_method_chain :save, :bullet
end

::ActiveRecord::Associations::Preloader.class_eval do
# include query for one to many associations.
# keep this eager loadings.
Expand Down
10 changes: 10 additions & 0 deletions lib/bullet/active_record41.rb
Expand Up @@ -23,6 +23,16 @@ def to_a
end
end

::ActiveRecord::Persistence.class_eval do
def save_with_bullet(*args, &proc)
was_new_record = new_record?
save_without_bullet(*args, &proc).tap do |result|
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
end
end
alias_method_chain :save, :bullet
end

::ActiveRecord::Associations::Preloader.class_eval do
alias_method :origin_preloaders_on, :preloaders_on

Expand Down
10 changes: 10 additions & 0 deletions lib/bullet/active_record42.rb
Expand Up @@ -21,6 +21,16 @@ def find(*args)
end
end

::ActiveRecord::Persistence.class_eval do
def save_with_bullet(*args, &proc)
was_new_record = new_record?
save_without_bullet(*args, &proc).tap do |result|
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
end
end
alias_method_chain :save, :bullet
end

::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.
Expand Down
6 changes: 6 additions & 0 deletions lib/bullet/dependency.rb
Expand Up @@ -36,6 +36,8 @@ def mongoid_version
'mongoid3x'
elsif mongoid4x?
'mongoid4x'
elsif mongoid5x?
'mongoid5x'
end
end
end
Expand Down Expand Up @@ -83,5 +85,9 @@ def mongoid3x?
def mongoid4x?
mongoid? && ::Mongoid::VERSION =~ /\A4/
end

def mongoid5x?
mongoid? && ::Mongoid::VERSION =~ /\A5/
end
end
end
2 changes: 1 addition & 1 deletion lib/bullet/detector/n_plus_one_query.rb
Expand Up @@ -70,7 +70,7 @@ def association?(object, associations)
# associations == v comparision 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.has_key?(associations) : associations == v
result = v.is_a?(Hash) ? v.key?(associations) : associations == v
return true if result
end
end
Expand Down
4 changes: 3 additions & 1 deletion lib/bullet/ext/object.rb
Expand Up @@ -4,7 +4,9 @@ def bullet_key
end

def primary_key_value
if self.class.respond_to?(:primary_key) && self.class.primary_key
if self.class.respond_to?(:primary_keys) && self.class.primary_keys
self.class.primary_keys.map { |primary_key| self.send primary_key }.join(',')
elsif self.class.respond_to?(:primary_key) && self.class.primary_key
self.send self.class.primary_key
else
self.id
Expand Down
56 changes: 56 additions & 0 deletions lib/bullet/mongoid5x.rb
@@ -0,0 +1,56 @@
module Bullet
module Mongoid
def self.enable
require 'mongoid'
::Mongoid::Contextual::Mongo.class_eval do
alias_method :origin_first, :first
alias_method :origin_last, :last
alias_method :origin_each, :each
alias_method :origin_eager_load, :eager_load

def first
result = origin_first
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
result
end

def last
result = origin_last
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
result
end

def each(&block)
records = view.map{ |doc| ::Mongoid::Factory.from_db(klass, doc) }
if records.length > 1
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
elsif records.size == 1
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
end
origin_each(&block)
end

def eager_load(docs)
associations = criteria.inclusions.map(&:name)
docs.each do |doc|
Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations)
end
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
origin_eager_load(docs)
end
end

::Mongoid::Relations::Accessors.class_eval do
alias_method :origin_get_relation, :get_relation

def get_relation(name, metadata, object, reload = false)
result = origin_get_relation(name, metadata, object, reload)
if metadata.macro !~ /embed/
Bullet::Detector::NPlusOneQuery.call_association(self, name)
end
result
end
end
end
end
end
6 changes: 6 additions & 0 deletions spec/bullet/ext/object_spec.rb
Expand Up @@ -27,5 +27,11 @@
expect(post.primary_key_value).to eq(post.name)
Post.primary_key = 'id'
end

it "should return value for multiple primary keys" do
post = Post.first
allow(Post).to receive(:primary_keys).and_return([:category_id, :writer_id])
expect(post.primary_key_value).to eq("#{post.category_id},#{post.writer_id}")
end
end
end
47 changes: 47 additions & 0 deletions spec/bullet_spec.rb
Expand Up @@ -38,4 +38,51 @@
end
end
end

describe '#start?' do
context 'when bullet is disabled' do
before(:each) do
Bullet.enable = false
end

it 'should not be started' do
expect(Bullet).not_to be_start
end
end
end

describe '#debug' do
before(:each) do
$stdout = StringIO.new
end

after(:each) do
$stdout = STDOUT
end

context 'when debug is enabled' do
before(:each) do
ENV['BULLET_DEBUG'] = 'true'
end

after(:each) do
ENV['BULLET_DEBUG'] = 'false'
end

it 'should output debug information' do
Bullet.debug('debug_message', 'this is helpful information')

expect($stdout.string)
.to eq("[Bullet][debug_message] this is helpful information\n")
end
end

context 'when debug is disabled' do
it 'should output debug information' do
Bullet.debug('debug_message', 'this is helpful information')

expect($stdout.string).to be_empty
end
end
end
end
21 changes: 21 additions & 0 deletions spec/integration/active_record4/association_spec.rb
Expand Up @@ -560,6 +560,27 @@
end
end

describe Bullet::Detector::Association, "query immediately after creation" do
context "document => children" do
it 'should not detect non preload associations' do
document1 = Document.new
document1.children.build
document1.save

document2 = Document.new(parent: document1)
document2.save
document2.parent

document1.children.each.first

Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations

expect(Bullet::Detector::Association).to be_completely_preloading_associations
end
end
end

describe Bullet::Detector::Association, "STI" do
context "page => author" do
it "should detect non preload associations" do
Expand Down
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Expand Up @@ -58,6 +58,7 @@ def env

config.before(:example) do
Bullet.start_request
Bullet.enable = true
end

config.after(:example) do
Expand Down
13 changes: 13 additions & 0 deletions spec/support/mongo_seed.rb
Expand Up @@ -52,6 +52,19 @@ def setup_db
}
)
end
elsif Mongoid::VERSION =~ /\A5/
Mongoid.configure do |config|
config.load_configuration(
clients: {
default: {
database: "bullet",
hosts: [ "localhost:27017" ]
}
}
)
end
# Increase the level from DEBUG in order to avoid excessive logging to the screen
Mongo::Logger.logger.level = Logger::WARN
end
end

Expand Down

0 comments on commit 1e20e0d

Please sign in to comment.