bullet 4.13.1 → 4.14.3

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +17 -2
  4. data/Gemfile.mongoid-2.4 +2 -2
  5. data/Gemfile.mongoid-2.5 +2 -2
  6. data/Gemfile.mongoid-2.6 +1 -1
  7. data/Gemfile.mongoid-2.7 +2 -2
  8. data/Gemfile.mongoid-2.8 +2 -2
  9. data/Gemfile.mongoid-3.0 +2 -2
  10. data/Gemfile.mongoid-3.1 +2 -2
  11. data/Gemfile.mongoid-4.0 +1 -1
  12. data/Gemfile.rails-3.0 +1 -3
  13. data/Gemfile.rails-3.1 +1 -1
  14. data/Gemfile.rails-3.2 +1 -1
  15. data/Gemfile.rails-4.0 +1 -1
  16. data/Gemfile.rails-4.1 +1 -1
  17. data/Gemfile.rails-4.2 +19 -0
  18. data/README.md +2 -1
  19. data/lib/bullet/active_record4.rb +4 -2
  20. data/lib/bullet/active_record41.rb +2 -2
  21. data/lib/bullet/active_record42.rb +152 -0
  22. data/lib/bullet/dependency.rb +6 -0
  23. data/lib/bullet/detector/unused_eager_loading.rb +1 -1
  24. data/lib/bullet/ext/object.rb +1 -1
  25. data/lib/bullet/notification/base.rb +7 -11
  26. data/lib/bullet/notification/n_plus_one_query.rb +0 -4
  27. data/lib/bullet/rack.rb +16 -7
  28. data/lib/bullet/version.rb +1 -1
  29. data/lib/bullet.rb +1 -1
  30. data/spec/bullet/notification/base_spec.rb +2 -27
  31. data/spec/bullet/notification/n_plus_one_query_spec.rb +3 -3
  32. data/spec/bullet/notification/unused_eager_loading_spec.rb +1 -1
  33. data/spec/bullet/rack_spec.rb +1 -1
  34. data/spec/integration/active_record4/association_spec.rb +26 -1
  35. data/spec/models/category.rb +4 -1
  36. data/spec/models/comment.rb +2 -0
  37. data/spec/models/post.rb +7 -2
  38. data/spec/models/reply.rb +3 -0
  39. data/spec/models/submission.rb +1 -1
  40. data/spec/spec_helper.rb +2 -2
  41. data/spec/support/sqlite_seed.rb +14 -6
  42. data/test.sh +1 -0
  43. data/update.sh +14 -0
  44. metadata +8 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c4c663f19d349dc9be3a82803ba5e5af344ed0e2
4
- data.tar.gz: f996a53cd2ce05eb85f11e55477b41d7b6f841f8
3
+ metadata.gz: dbe02316f8b3e0b61718c940420f189423c3f5f0
4
+ data.tar.gz: 93db165a6618fb9c470081013e4b2f5fe002c602
5
5
  SHA512:
6
- metadata.gz: 9fe6ad4a5e0b23c19815a121e8b3dec2989c04f2e81836eba4a096a06b68ae7b33c7c202c1a7a99e697a48bd5bd29711b7ad0a2b17e1d203e32cc964a0e95314
7
- data.tar.gz: 77e4bc950eaa4e0e87fd32beaa6f11a605e1e443b0de0f3fbea89798cd11721c3c6a4fa0c9f70bad5c3b7f65586a56b050e38265a21d57d57244e219ab0f8501
6
+ metadata.gz: 6c9ec0b4e3c23e6de5f59f74cb9378f5db7c32a448a42d4304b2399c3762308bab649dffc7f86bd04aaf175946391df9658994a1a190fb2a64fb8ec5bd21dbb3
7
+ data.tar.gz: 47451dec206f2d05aaa11466d8f6c063bbb9e3e9c03929d7dc8867b11e77be6871ee6a58f1f2b52435db3bb6854e913228dc9b310bf7dd78f5b968de5f042932
data/.travis.yml CHANGED
@@ -2,6 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 2.1.2
4
4
  gemfile:
5
+ - Gemfile.rails-4.2
5
6
  - Gemfile.rails-4.1
6
7
  - Gemfile.rails-4.0
7
8
  - Gemfile.rails-3.2
data/CHANGELOG.md CHANGED
@@ -1,8 +1,23 @@
1
1
  # Next Release
2
2
 
3
- ## 4.13.1
3
+ ## 4.14.3
4
4
 
5
- * Fix swallow exception issue
5
+ * Fix false positive on create
6
+
7
+ ## 4.14.2
8
+
9
+ * Hotfix nil object when add_impossible_object
10
+
11
+ ## 4.14.1
12
+
13
+ * Fix has_one then has_many associations in rails 4.2
14
+ * Append js and dom to html body in proper position
15
+
16
+ ## 4.14.0 (10/03/2014)
17
+
18
+ * Support rails 4.2
19
+ * Polish notification output
20
+ * Fix warning: `*' interpreted as argument prefix
6
21
 
7
22
  ## 4.13.0 (07/19/2014)
8
23
 
data/Gemfile.mongoid-2.4 CHANGED
@@ -2,10 +2,10 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 3.2.16'
5
+ gem 'rails', '~> 3.2.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
- gem 'mongoid', '~> 2.4.12'
8
+ gem 'mongoid', '~> 2.4.0'
9
9
 
10
10
  gem "rspec"
11
11
  gem "guard"
data/Gemfile.mongoid-2.5 CHANGED
@@ -2,10 +2,10 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 3.2.16'
5
+ gem 'rails', '~> 3.2.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
- gem 'mongoid', '~> 2.5.2'
8
+ gem 'mongoid', '~> 2.5.0'
9
9
 
10
10
  gem "rspec"
11
11
  gem "guard"
data/Gemfile.mongoid-2.6 CHANGED
@@ -2,7 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 3.2.16'
5
+ gem 'rails', '~> 3.2.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
8
  gem 'mongoid', '~> 2.6.0'
data/Gemfile.mongoid-2.7 CHANGED
@@ -2,10 +2,10 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 3.2.16'
5
+ gem 'rails', '~> 3.2.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
- gem 'mongoid', '~> 2.7.1'
8
+ gem 'mongoid', '~> 2.7.0'
9
9
 
10
10
  gem "rspec"
11
11
  gem "guard"
data/Gemfile.mongoid-2.8 CHANGED
@@ -2,10 +2,10 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 3.2'
5
+ gem 'rails', '~> 3.2.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
- gem 'mongoid', '~> 2.8'
8
+ gem 'mongoid', '~> 2.8.0'
9
9
 
10
10
  gem "rspec"
11
11
  gem "guard"
data/Gemfile.mongoid-3.0 CHANGED
@@ -2,10 +2,10 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 3.2.16'
5
+ gem 'rails', '~> 3.2.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
- gem 'mongoid', '~> 3.0.23'
8
+ gem 'mongoid', '~> 3.0.0'
9
9
 
10
10
  gem "rspec"
11
11
  gem "guard"
data/Gemfile.mongoid-3.1 CHANGED
@@ -2,10 +2,10 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 3.2.16'
5
+ gem 'rails', '~> 3.2.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
- gem 'mongoid', '~> 3.1.6'
8
+ gem 'mongoid', '~> 3.1.0'
9
9
 
10
10
  gem "rspec"
11
11
  gem "guard"
data/Gemfile.mongoid-4.0 CHANGED
@@ -2,7 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 4.0.5'
5
+ gem 'rails', '~> 4.0.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
8
  gem 'mongoid', '~> 4.0.0'
data/Gemfile.rails-3.0 CHANGED
@@ -2,14 +2,12 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 3.0.20'
5
+ gem 'rails', '~> 3.0.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
8
  gem 'activerecord-import'
9
9
 
10
10
  gem "rspec"
11
- gem "guard"
12
- gem "guard-rspec"
13
11
 
14
12
  gem 'coveralls', require: false
15
13
 
data/Gemfile.rails-3.1 CHANGED
@@ -2,7 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 3.1.12'
5
+ gem 'rails', '~> 3.1.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
8
  gem 'activerecord-import'
data/Gemfile.rails-3.2 CHANGED
@@ -2,7 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 3.2.19'
5
+ gem 'rails', '~> 3.2.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
8
  gem 'activerecord-import'
data/Gemfile.rails-4.0 CHANGED
@@ -2,7 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 4.0.8'
5
+ gem 'rails', '~> 4.0.0'
6
6
  gem 'sqlite3', platforms: [:ruby]
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
8
  gem 'activerecord-import'
data/Gemfile.rails-4.1 CHANGED
@@ -2,7 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'rails', '~> 4.1.4'
5
+ gem 'rails', '~> 4.1.0'
6
6
  gem 'sqlite3'
7
7
  gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
8
  gem 'activerecord-import'
data/Gemfile.rails-4.2 ADDED
@@ -0,0 +1,19 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem 'rails', '~> 4.2.0'
6
+ gem 'sqlite3'
7
+ gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
8
+ gem 'activerecord-import'
9
+
10
+ gem "rspec"
11
+ gem "guard"
12
+ gem "guard-rspec"
13
+
14
+ gem 'coveralls', require: false
15
+
16
+ platforms :rbx do
17
+ gem 'rubysl', '~> 2.0'
18
+ gem 'rubinius-developer_tools'
19
+ end
data/README.md CHANGED
@@ -67,6 +67,7 @@ The code above will enable all seven of the Bullet notification systems:
67
67
  * `Bullet.alert`: pop up a JavaScript alert in the browser
68
68
  * `Bullet.bullet_logger`: log to the Bullet log file (Rails.root/log/bullet.log)
69
69
  * `Bullet.rails_logger`: add warnings directly to the Rails log
70
+ * `Bullet.bugsnag`: add notifications to bugsnag
70
71
  * `Bullet.airbrake`: add notifications to airbrake
71
72
  * `Bullet.console`: log warnings to your browser's console.log (Safari/Webkit browsers or Firefox w/Firebug installed)
72
73
  * `Bullet.growl`: pop up Growl warnings if your system has Growl installed. Requires a little bit of configuration
@@ -429,4 +430,4 @@ Meanwhile, there's a line appended to `log/bullet.log`
429
430
  Post => [:comments]
430
431
  ```
431
432
 
432
- Copyright (c) 2009 - 2014 Richard Huang (flyerhzm@gmail.com), released under the MIT license
433
+ Copyright (c) 2009 - 2015 Richard Huang (flyerhzm@gmail.com), released under the MIT license
@@ -104,8 +104,10 @@ module Bullet
104
104
  alias_method :origin_reader, :reader
105
105
  def reader(force_reload = false)
106
106
  result = origin_reader(force_reload)
107
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
108
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
107
+ unless @inversed
108
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
109
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
110
+ end
109
111
  result
110
112
  end
111
113
  end
@@ -106,8 +106,8 @@ module Bullet
106
106
  alias_method :origin_reader, :reader
107
107
  def reader(force_reload = false)
108
108
  result = origin_reader(force_reload)
109
- if @owner.class.name !~ /^HABTM_/
110
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
109
+ if @owner.class.name !~ /^HABTM_/ && !@inversed
110
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
111
111
  Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
112
112
  end
113
113
  result
@@ -0,0 +1,152 @@
1
+ module Bullet
2
+ module ActiveRecord
3
+ def self.enable
4
+ require 'active_record'
5
+ ::ActiveRecord::Relation.class_eval do
6
+ alias_method :origin_to_a, :to_a
7
+ # if select a collection of objects, then these objects have possible to cause N+1 query.
8
+ # if select only one object, then the only one object has impossible to cause N+1 query.
9
+ def to_a
10
+ records = origin_to_a
11
+ if records.first.class.name !~ /^HABTM_/
12
+ if records.size > 1
13
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
14
+ Bullet::Detector::CounterCache.add_possible_objects(records)
15
+ elsif records.size == 1
16
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
17
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
18
+ end
19
+ end
20
+ records
21
+ end
22
+ end
23
+
24
+ ::ActiveRecord::Associations::Preloader.class_eval do
25
+ alias_method :origin_preloaders_on, :preloaders_on
26
+
27
+ def preloaders_on(association, records, scope)
28
+ records.compact!
29
+ if records.first.class.name !~ /^HABTM_/
30
+ records.each do |record|
31
+ Bullet::Detector::Association.add_object_associations(record, association)
32
+ end
33
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
34
+ end
35
+ origin_preloaders_on(association, records, scope)
36
+ end
37
+ end
38
+
39
+ ::ActiveRecord::FinderMethods.class_eval do
40
+ # add includes in scope
41
+ alias_method :origin_find_with_associations, :find_with_associations
42
+ def find_with_associations
43
+ records = origin_find_with_associations
44
+ associations = (eager_load_values + includes_values).uniq
45
+ records.each do |record|
46
+ Bullet::Detector::Association.add_object_associations(record, associations)
47
+ end
48
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
49
+ records
50
+ end
51
+ end
52
+
53
+ ::ActiveRecord::Associations::JoinDependency.class_eval do
54
+ alias_method :origin_instantiate, :instantiate
55
+ alias_method :origin_construct_model, :construct_model
56
+
57
+ def instantiate(result_set, aliases)
58
+ @bullet_eager_loadings = {}
59
+ records = origin_instantiate(result_set, aliases)
60
+
61
+ @bullet_eager_loadings.each do |klazz, eager_loadings_hash|
62
+ objects = eager_loadings_hash.keys
63
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
64
+ end
65
+ records
66
+ end
67
+
68
+ # call join associations
69
+ def construct_model(record, node, row, model_cache, id, aliases)
70
+ result = origin_construct_model(record, node, row, model_cache, id, aliases)
71
+
72
+ associations = node.reflection.name
73
+ Bullet::Detector::Association.add_object_associations(record, associations)
74
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
75
+ @bullet_eager_loadings[record.class] ||= {}
76
+ @bullet_eager_loadings[record.class][record] ||= Set.new
77
+ @bullet_eager_loadings[record.class][record] << associations
78
+
79
+ result
80
+ end
81
+ end
82
+
83
+ ::ActiveRecord::Associations::CollectionAssociation.class_eval do
84
+ # call one to many associations
85
+ alias_method :origin_load_target, :load_target
86
+ def load_target
87
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
88
+ records = origin_load_target
89
+
90
+ if records.first.class.name !~ /^HABTM_/
91
+ if records.size > 1
92
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
93
+ Bullet::Detector::CounterCache.add_possible_objects(records)
94
+ elsif records.size == 1
95
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
96
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
97
+ end
98
+ end
99
+ records
100
+ end
101
+
102
+ alias_method :origin_empty?, :empty?
103
+ def empty?
104
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
105
+ origin_empty?
106
+ end
107
+
108
+ alias_method :origin_include?, :include?
109
+ def include?(object)
110
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
111
+ origin_include?(object)
112
+ end
113
+ end
114
+
115
+ ::ActiveRecord::Associations::SingularAssociation.class_eval do
116
+ # call has_one and belongs_to associations
117
+ alias_method :origin_reader, :reader
118
+ def reader(force_reload = false)
119
+ result = origin_reader(force_reload)
120
+ if @owner.class.name !~ /^HABTM_/ && !@inversed
121
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
122
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
123
+ end
124
+ if ::ActiveRecord::Reflection::HasOneReflection === @reflection && result
125
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
126
+ end
127
+ result
128
+ end
129
+ end
130
+
131
+ ::ActiveRecord::Associations::HasManyAssociation.class_eval do
132
+ alias_method :origin_many_empty?, :empty?
133
+ def empty?
134
+ Thread.current[:bullet_collection_empty] = true
135
+ result = origin_many_empty?
136
+ Thread.current[:bullet_collection_empty] = nil
137
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
138
+ result
139
+ end
140
+
141
+ alias_method :origin_has_cached_counter?, :has_cached_counter?
142
+ def has_cached_counter?(reflection = reflection())
143
+ result = origin_has_cached_counter?(reflection)
144
+ if !result && !Thread.current[:bullet_collection_empty]
145
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
146
+ end
147
+ result
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -22,6 +22,8 @@ module Bullet
22
22
  'active_record4'
23
23
  elsif active_record41?
24
24
  'active_record41'
25
+ elsif active_record42?
26
+ 'active_record42'
25
27
  end
26
28
  end
27
29
  end
@@ -66,6 +68,10 @@ module Bullet
66
68
  active_record4? && ::ActiveRecord::VERSION::MINOR == 1
67
69
  end
68
70
 
71
+ def active_record42?
72
+ active_record4? && ::ActiveRecord::VERSION::MINOR == 2
73
+ end
74
+
69
75
  def mongoid2x?
70
76
  mongoid? && ::Mongoid::VERSION =~ /\A2\.[4-8]/
71
77
  end
@@ -47,7 +47,7 @@ module Bullet
47
47
  end
48
48
  end
49
49
 
50
- eager_loadings.add *to_add if to_add
50
+ eager_loadings.add(*to_add) if to_add
51
51
  to_merge.each { |k,val| eager_loadings.merge k, val }
52
52
  to_delete.each { |k| eager_loadings.delete k }
53
53
 
@@ -1,6 +1,6 @@
1
1
  class Object
2
2
  def bullet_key
3
- [self.class, self.primary_key_value].join(':')
3
+ "#{self.class}:#{self.primary_key_value}"
4
4
  end
5
5
 
6
6
  def primary_key_value
@@ -18,6 +18,10 @@ module Bullet
18
18
  raise NoMethodError.new("no method body defined")
19
19
  end
20
20
 
21
+ def call_stack_messages
22
+ ""
23
+ end
24
+
21
25
  def whoami
22
26
  @user ||= ENV['USER'].presence || (`whoami`.chomp rescue "")
23
27
  if @user.present?
@@ -28,15 +32,7 @@ module Bullet
28
32
  end
29
33
 
30
34
  def body_with_caller
31
- body
32
- end
33
-
34
- def standard_notice
35
- @standard_notifice ||= title + "\n" + body
36
- end
37
-
38
- def full_notice
39
- [whoami.presence, url, title, body_with_caller].compact.join("\n")
35
+ "#{body}\n#{call_stack_messages}\n"
40
36
  end
41
37
 
42
38
  def notify_inline
@@ -48,7 +44,7 @@ module Bullet
48
44
  end
49
45
 
50
46
  def short_notice
51
- [whoami.presence, url, title, body].compact.join("\n")
47
+ [whoami.presence, url, title, body].compact.join(" ")
52
48
  end
53
49
 
54
50
  def notification_data
@@ -74,7 +70,7 @@ module Bullet
74
70
  end
75
71
 
76
72
  def associations_str
77
- ":include => #{@associations.map{ |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
73
+ ":includes => #{@associations.map{ |a| a.to_s.to_sym unless a.is_a? Hash }.inspect}"
78
74
  end
79
75
  end
80
76
  end
@@ -7,10 +7,6 @@ module Bullet
7
7
  @callers = callers
8
8
  end
9
9
 
10
- def body_with_caller
11
- "#{body}\n#{call_stack_messages}"
12
- end
13
-
14
10
  def body
15
11
  "#{klazz_associations_str}\n Add to your finder: #{associations_str}"
16
12
  end
data/lib/bullet/rack.rb CHANGED
@@ -15,8 +15,9 @@ module Bullet
15
15
  response_body = nil
16
16
  if Bullet.notification?
17
17
  if status == 200 && !response_body(response).frozen? && html_request?(headers, response)
18
- response_body = response_body(response) << Bullet.gather_inline_notifications
19
- add_footer_note(response_body) if Bullet.add_footer
18
+ response_body = response_body(response)
19
+ append_to_html_body(response_body, footer_note) if Bullet.add_footer
20
+ append_to_html_body(response_body, Bullet.gather_inline_notifications)
20
21
  headers['Content-Length'] = response_body.bytesize.to_s
21
22
  end
22
23
  end
@@ -42,8 +43,17 @@ module Bullet
42
43
  end
43
44
  end
44
45
 
45
- def add_footer_note(response_body)
46
- response_body << "<div #{footer_div_style}>" + Bullet.footer_info.uniq.join("<br>") + "</div>"
46
+ def append_to_html_body(response_body, content)
47
+ if response_body.include?('</body>')
48
+ position = response_body.rindex('</body>')
49
+ response_body.insert(position, content)
50
+ else
51
+ response_body << content
52
+ end
53
+ end
54
+
55
+ def footer_note
56
+ "<div #{footer_div_attributes}>" + Bullet.footer_info.uniq.join("<br>") + "</div>"
47
57
  end
48
58
 
49
59
  def file?(headers)
@@ -67,9 +77,9 @@ module Bullet
67
77
  end
68
78
 
69
79
  private
70
- def footer_div_style
80
+ def footer_div_attributes
71
81
  <<EOF
72
- style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
82
+ data-is-bullet-footer ondblclick="this.parentNode.removeChild(this);" style="position: fixed; bottom: 0pt; left: 0pt; cursor: pointer; border-style: solid; border-color: rgb(153, 153, 153);
73
83
  -moz-border-top-colors: none; -moz-border-right-colors: none; -moz-border-bottom-colors: none;
74
84
  -moz-border-left-colors: none; -moz-border-image: none; border-width: 2pt 2pt 0px 0px;
75
85
  padding: 5px; border-radius: 0pt 10pt 0pt 0px; background: none repeat scroll 0% 0% rgba(200, 200, 200, 0.8);
@@ -78,4 +88,3 @@ EOF
78
88
  end
79
89
  end
80
90
  end
81
-
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Bullet
3
- VERSION = "4.13.1"
3
+ VERSION = "4.14.3"
4
4
  end
data/lib/bullet.rb CHANGED
@@ -153,7 +153,7 @@ module Bullet
153
153
 
154
154
  def perform_out_of_channel_notifications(env = {})
155
155
  for_each_active_notifier_with_notification do |notification|
156
- notification.url = [env['HTTP_HOST'], env['REQUEST_URI']].compact.join
156
+ notification.url = env['REQUEST_URI']
157
157
  notification.notify_out_of_channel
158
158
  end
159
159
  end
@@ -56,33 +56,8 @@ module Bullet
56
56
  context "#body_with_caller" do
57
57
  it "should return body" do
58
58
  allow(subject).to receive(:body).and_return("body")
59
- expect(subject.body_with_caller).to eq("body")
60
- end
61
- end
62
-
63
- context "#standard_notice" do
64
- it "should return title + body" do
65
- allow(subject).to receive(:title).and_return("title")
66
- allow(subject).to receive(:body).and_return("body")
67
- expect(subject.standard_notice).to eq("title\nbody")
68
- end
69
- end
70
-
71
- context "#full_notice" do
72
- it "should return whoami + url + title + body_with_caller" do
73
- allow(subject).to receive(:whoami).and_return("whoami")
74
- allow(subject).to receive(:url).and_return("url")
75
- allow(subject).to receive(:title).and_return("title")
76
- allow(subject).to receive(:body_with_caller).and_return("body_with_caller")
77
- expect(subject.full_notice).to eq("whoami\nurl\ntitle\nbody_with_caller")
78
- end
79
-
80
- it "should return url + title + body_with_caller" do
81
- allow(subject).to receive(:whoami).and_return("")
82
- allow(subject).to receive(:url).and_return("url")
83
- allow(subject).to receive(:title).and_return("title")
84
- allow(subject).to receive(:body_with_caller).and_return("body_with_caller")
85
- expect(subject.full_notice).to eq("url\ntitle\nbody_with_caller")
59
+ allow(subject).to receive(:call_stack_messages).and_return("call_stack_messages")
60
+ expect(subject.body_with_caller).to eq("body\ncall_stack_messages\n")
86
61
  end
87
62
  end
88
63
 
@@ -5,9 +5,9 @@ module Bullet
5
5
  describe NPlusOneQuery do
6
6
  subject { NPlusOneQuery.new([["caller1", "caller2"]], Post, [:comments, :votes], "path") }
7
7
 
8
- it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2") }
9
- it { expect([ subject.body_with_caller, subject.body_with_caller]).to eq([ " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2", " Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2" ]) }
10
- it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your finder: :include => [:comments, :votes]") }
8
+ it { expect(subject.body_with_caller).to eq(" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n") }
9
+ it { expect([subject.body_with_caller, subject.body_with_caller]).to eq([ " Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n", " Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]\nN+1 Query method call stack\n caller1\n caller2\n" ]) }
10
+ it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Add to your finder: :includes => [:comments, :votes]") }
11
11
  it { expect(subject.title).to eq("N+1 Query in path") }
12
12
  end
13
13
  end
@@ -5,7 +5,7 @@ module Bullet
5
5
  describe UnusedEagerLoading do
6
6
  subject { UnusedEagerLoading.new(Post, [:comments, :votes], "path") }
7
7
 
8
- it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Remove from your finder: :include => [:comments, :votes]") }
8
+ it { expect(subject.body).to eq(" Post => [:comments, :votes]\n Remove from your finder: :includes => [:comments, :votes]") }
9
9
  it { expect(subject.title).to eq("Unused Eager Loading in path") }
10
10
  end
11
11
  end
@@ -70,7 +70,7 @@ module Bullet
70
70
  expect(Bullet).to receive(:perform_out_of_channel_notifications)
71
71
  status, headers, response = middleware.call([200, {"Content-Type" => "text/html"}])
72
72
  expect(headers["Content-Length"]).to eq("56")
73
- expect(response).to eq(["<html><head></head><body></body></html><bullet></bullet>"])
73
+ expect(response).to eq(["<html><head></head><body><bullet></bullet></body></html>"])
74
74
  end
75
75
 
76
76
  it "should set the right Content-Length if response body contains accents" do
@@ -219,6 +219,20 @@ if !mongoid? && active_record4?
219
219
 
220
220
  expect(Bullet::Detector::Association).to be_completely_preloading_associations
221
221
  end
222
+
223
+ it "should not detect unused preload with category => posts" do
224
+ category = Category.first
225
+ category.draft_post.destroy!
226
+ post = category.draft_post
227
+ post.update_attributes!(link: true)
228
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
229
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
230
+
231
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
232
+
233
+ Support::SqliteSeed.setup_db
234
+ Support::SqliteSeed.seed_db
235
+ end
222
236
  end
223
237
 
224
238
  context "category => posts => writer" do
@@ -313,7 +327,7 @@ if !mongoid? && active_record4?
313
327
  expect(Bullet::Detector::Association).to be_completely_preloading_associations
314
328
  end
315
329
 
316
- it "should detect unused preload with comments => post" do
330
+ it "should detect unused preload with comment => post" do
317
331
  Comment.includes(:post).map(&:name)
318
332
  Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
319
333
  expect(Bullet::Detector::Association).to be_unused_preload_associations_for(Comment, :post)
@@ -515,6 +529,17 @@ if !mongoid? && active_record4?
515
529
  end
516
530
  end
517
531
 
532
+ describe Bullet::Detector::Association, "has_one => has_many" do
533
+ it "should not detect preload association" do
534
+ user = User.first
535
+ user.submission.replies.map(&:name)
536
+ Bullet::Detector::UnusedEagerLoading.check_unused_preload_associations
537
+ expect(Bullet::Detector::Association).not_to be_has_unused_preload_associations
538
+
539
+ expect(Bullet::Detector::Association).to be_completely_preloading_associations
540
+ end
541
+ end
542
+
518
543
  describe Bullet::Detector::Association, "call one association that in possible objects" do
519
544
  it "should not detect preload association" do
520
545
  Post.all
@@ -2,6 +2,9 @@ class Category < ActiveRecord::Base
2
2
  has_many :posts, inverse_of: :category
3
3
  has_many :entries
4
4
 
5
- has_many :submissions
6
5
  has_many :users
6
+
7
+ def draft_post
8
+ posts.draft.first_or_create
9
+ end
7
10
  end
@@ -1,4 +1,6 @@
1
1
  class Comment < ActiveRecord::Base
2
2
  belongs_to :post, inverse_of: :comments
3
3
  belongs_to :author, class_name: "BaseUser"
4
+
5
+ validates :post, presence: true
4
6
  end
data/spec/models/post.rb CHANGED
@@ -1,10 +1,15 @@
1
1
  class Post < ActiveRecord::Base
2
- extend Bullet::Dependency
3
-
4
2
  belongs_to :category, inverse_of: :posts
5
3
  belongs_to :writer
6
4
  has_many :comments, inverse_of: :post
7
5
 
6
+ validates :category, presence: true
7
+
8
8
  scope :preload_comments, -> { includes(:comments) }
9
9
  scope :in_category_name, ->(name) { where(['categories.name = ?', name]).includes(:category) }
10
+ scope :draft, -> { where(active: false) }
11
+
12
+ def link=(*)
13
+ comments.new
14
+ end
10
15
  end
@@ -0,0 +1,3 @@
1
+ class Reply < ActiveRecord::Base
2
+ belongs_to :submission
3
+ end
@@ -1,4 +1,4 @@
1
1
  class Submission < ActiveRecord::Base
2
- belongs_to :category
3
2
  belongs_to :user
3
+ has_many :replies
4
4
  end
data/spec/spec_helper.rb CHANGED
@@ -56,11 +56,11 @@ if active_record?
56
56
  Support::SqliteSeed.seed_db
57
57
  end
58
58
 
59
- config.before(:each) do
59
+ config.before(:example) do
60
60
  Bullet.start_request
61
61
  end
62
62
 
63
- config.after(:each) do
63
+ config.after(:example) do
64
64
  Bullet.end_request
65
65
  end
66
66
  end
@@ -14,7 +14,7 @@ module Support
14
14
  category2 = Category.create(:name => 'second')
15
15
 
16
16
  post1 = category1.posts.create(:name => 'first', :writer => writer1)
17
- post1a = category1.posts.create(:name => 'like first', :writer => writer2)
17
+ post1a = category1.posts.create(:name => 'like first', :writer => writer2, active: false)
18
18
  post2 = category2.posts.create(:name => 'second', :writer => writer2)
19
19
 
20
20
  comment1 = post1.comments.create(:name => 'first', :author => writer1)
@@ -82,10 +82,13 @@ module Support
82
82
  user1 = User.create(:name => 'user1', :category => category1)
83
83
  user2 = User.create(:name => 'user2', :category => category1)
84
84
 
85
- submission1 = category1.submissions.create(:name => "submission1", :user => user1)
86
- submission2 = category1.submissions.create(:name => "submission2", :user => user2)
87
- submission3 = category2.submissions.create(:name => "submission3", :user => user1)
88
- submission4 = category2.submissions.create(:name => "submission4", :user => user2)
85
+ submission1 = user1.create_submission(:name => "submission1")
86
+ submission2 = user2.create_submission(:name => "submission2")
87
+
88
+ submission1.replies.create(:name => 'reply1')
89
+ submission1.replies.create(:name => 'reply2')
90
+ submission2.replies.create(:name => 'reply3')
91
+ submission2.replies.create(:name => 'reply4')
89
92
  end
90
93
 
91
94
  def setup_db
@@ -191,6 +194,7 @@ module Support
191
194
  t.column :name, :string
192
195
  t.column :category_id, :integer
193
196
  t.column :writer_id, :integer
197
+ t.column :active, :boolean, :default => true
194
198
  end
195
199
 
196
200
  create_table :relationships do |t|
@@ -211,9 +215,13 @@ module Support
211
215
  t.column :name, :string
212
216
  end
213
217
 
218
+ create_table :replies do |t|
219
+ t.column :name, :string
220
+ t.column :submission_id, :integer
221
+ end
222
+
214
223
  create_table :submissions do |t|
215
224
  t.column :name, :string
216
- t.column :category_id, :integer
217
225
  t.column :user_id, :integer
218
226
  end
219
227
 
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-4.2 bundle && BUNDLE_GEMFILE=Gemfile.rails-4.2 bundle exec rspec spec
3
4
  BUNDLE_GEMFILE=Gemfile.rails-4.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-4.1 bundle exec rspec spec
4
5
  BUNDLE_GEMFILE=Gemfile.rails-4.0 bundle && BUNDLE_GEMFILE=Gemfile.rails-4.0 bundle exec rspec spec
5
6
  BUNDLE_GEMFILE=Gemfile.rails-3.2 bundle && BUNDLE_GEMFILE=Gemfile.rails-3.2 bundle exec rspec spec
data/update.sh ADDED
@@ -0,0 +1,14 @@
1
+ BUNDLE_GEMFILE=Gemfile.rails-4.2 bundle update
2
+ BUNDLE_GEMFILE=Gemfile.rails-4.1 bundle update
3
+ BUNDLE_GEMFILE=Gemfile.rails-4.0 bundle update
4
+ BUNDLE_GEMFILE=Gemfile.rails-3.2 bundle update
5
+ BUNDLE_GEMFILE=Gemfile.rails-3.1 bundle update
6
+ BUNDLE_GEMFILE=Gemfile.rails-3.0 bundle update
7
+ BUNDLE_GEMFILE=Gemfile.mongoid-4.0 bundle update
8
+ BUNDLE_GEMFILE=Gemfile.mongoid-3.1 bundle update
9
+ BUNDLE_GEMFILE=Gemfile.mongoid-3.0 bundle update
10
+ BUNDLE_GEMFILE=Gemfile.mongoid-2.8 bundle update
11
+ BUNDLE_GEMFILE=Gemfile.mongoid-2.7 bundle update
12
+ BUNDLE_GEMFILE=Gemfile.mongoid-2.6 bundle update
13
+ BUNDLE_GEMFILE=Gemfile.mongoid-2.5 bundle update
14
+ BUNDLE_GEMFILE=Gemfile.mongoid-2.4 bundle update
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: 4.13.1
4
+ version: 4.14.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-03 00:00:00.000000000 Z
11
+ date: 2015-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -64,6 +64,7 @@ files:
64
64
  - Gemfile.rails-3.2
65
65
  - Gemfile.rails-4.0
66
66
  - Gemfile.rails-4.1
67
+ - Gemfile.rails-4.2
67
68
  - Guardfile
68
69
  - Hacking.md
69
70
  - MIT-LICENSE
@@ -75,6 +76,7 @@ files:
75
76
  - lib/bullet/active_record3x.rb
76
77
  - lib/bullet/active_record4.rb
77
78
  - lib/bullet/active_record41.rb
79
+ - lib/bullet/active_record42.rb
78
80
  - lib/bullet/dependency.rb
79
81
  - lib/bullet/detector.rb
80
82
  - lib/bullet/detector/association.rb
@@ -148,6 +150,7 @@ files:
148
150
  - spec/models/pet.rb
149
151
  - spec/models/post.rb
150
152
  - spec/models/relationship.rb
153
+ - spec/models/reply.rb
151
154
  - spec/models/student.rb
152
155
  - spec/models/submission.rb
153
156
  - spec/models/teacher.rb
@@ -160,6 +163,7 @@ files:
160
163
  - spec/support/sqlite_seed.rb
161
164
  - tasks/bullet_tasks.rake
162
165
  - test.sh
166
+ - update.sh
163
167
  homepage: http://github.com/flyerhzm/bullet
164
168
  licenses:
165
169
  - MIT
@@ -180,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
184
  version: 1.3.6
181
185
  requirements: []
182
186
  rubyforge_project:
183
- rubygems_version: 2.2.2
187
+ rubygems_version: 2.4.5
184
188
  signing_key:
185
189
  specification_version: 4
186
190
  summary: help to kill N+1 queries and unused eager loading.
@@ -232,6 +236,7 @@ test_files:
232
236
  - spec/models/pet.rb
233
237
  - spec/models/post.rb
234
238
  - spec/models/relationship.rb
239
+ - spec/models/reply.rb
235
240
  - spec/models/student.rb
236
241
  - spec/models/submission.rb
237
242
  - spec/models/teacher.rb