lib/bullet/association.rb in flyerhzm-bullet-1.1.0 vs lib/bullet/association.rb in flyerhzm-bullet-1.2.0
- old
+ new
@@ -1,12 +1,18 @@
module Bullet
+ class BulletAssociationError < StandardError
+ end
+
class Association
class <<self
- @@logger_file = File.open(Bullet::BulletLogger::LOG_FILE, 'a+')
- @@logger = Bullet::BulletLogger.new(@@logger_file)
- @@alert = true
-
+ @@alert = nil
+ @@bullet_logger = nil
+ @@console = nil
+ @@growl = nil
+ @@growl_password = nil
+ @@rails_logger = nil
+
def start_request
# puts "start request"
end
def end_request
@@ -22,23 +28,46 @@
def alert=(alert)
@@alert = alert
end
- def logger=(logger)
- if logger == false
- @@logger = nil
+ def bullet_logger=(bullet_logger)
+ if @@bullet_logger = bullet_logger
+ @@logger_file = File.open(Bullet::BulletLogger::LOG_FILE, 'a+')
+ @@logger = Bullet::BulletLogger.new(@@logger_file)
end
end
-
+
+ def console=(console)
+ @@console = console
+ end
+
+ def growl=(growl)
+ if growl
+ begin
+ require 'ruby-growl'
+ growl = Growl.new('localhost', 'ruby-growl', ['Bullet Notification'], nil, @@growl_password)
+ growl.notify('Bullet Notification', 'Bullet Notification', 'Bullet Growl notifications have been turned on')
+ rescue MissingSourceFile
+ raise BulletAssociationError.new('You must install the ruby-growl gem to use Growl notifications: `sudo gem install ruby-growl`')
+ end
+ end
+ @@growl = growl
+ end
+
+ def growl_password=(growl_password)
+ @@growl_password = growl_password
+ end
+
+ def rails_logger=(rails_logger)
+ @@rails_logger = rails_logger
+ end
+
def check_unused_preload_associations
object_associations.each do |object, association|
- call_association = call_object_associations[object] || []
- association.uniq! unless association.flatten!.nil?
- call_association.uniq! unless call_association.flatten!.nil?
-
- add_unused_preload_associations(object.class, association - call_association) unless (association - call_association).empty?
+ call_object_association = call_object_associations[object] || []
+ add_unused_preload_associations(object.class, association - call_object_association) unless (association - call_object_association).empty?
end
end
def has_bad_assocations?
check_unused_preload_associations
@@ -53,64 +82,94 @@
!unpreload_associations.empty?
end
def bad_associations_alert
str = ''
+ if @@alert || @@console || @@growl
+ response = []
+ if has_unused_preload_associations?
+ response.push("Unused eager loadings detected:\n")
+ response.push(*@@unused_preload_associations.to_a.collect{|klazz, associations| klazz_associations_str(klazz, associations)}.join('\n'))
+ end
+ if has_unpreload_associations?
+ response.push("#{"\n" unless response.empty?}N+1 queries detected:\n")
+ response.push(*@@unpreload_associations.to_a.collect{|klazz, associations| " #{klazz} => [#{associations.map(&:inspect).join(', ')}]"}.join('\n'))
+ end
+ end
if @@alert
- str = "<script type='text/javascript'>"
- str << "alert('The request has unused preload assocations as follows:\\n"
- str << (has_unused_preload_associations? ? bad_associations_str(unused_preload_associations) : "None")
- str << "\\nThe request has N+1 queries as follows:\\n"
- str << (has_unpreload_associations? ? bad_associations_str(unpreload_associations) : "None")
- str << "')"
- str << "</script>\n"
+ str << wrap_js_association("alert(#{response.join("\n").inspect});")
end
+ if @@console
+ str << wrap_js_association("if (typeof(console) != 'undefined' && console.log) console.log(#{response.join("\n").inspect});")
+ end
+ if @@growl
+ begin
+ growl = Growl.new('localhost', 'ruby-growl', ['Bullet Notification'], nil, @@growl_password)
+ growl.notify('Bullet Notification', 'Bullet Notification', response.join("\n"))
+ rescue
+ end
+ str << '<!-- Sent Growl notification -->'
+ end
str
end
+
+ def wrap_js_association(message)
+ str = ''
+ str << "<script type=\"text/javascript\">/*<![CDATA[*/"
+ str << message
+ str << "/*]]>*/</script>\n"
+ end
+
+ def log_bad_associations(path)
+ if (@@bullet_logger || @@rails_logger) && (!unpreload_associations.empty? || !unused_preload_associations.empty?)
+ Rails.logger.warn '' if @@rails_logger
+ unused_preload_associations.each do |klazz, associations|
+ log = ["Unused eager loadings: #{path}", klazz_associations_str(klazz, associations), " Remove from your finder: #{associations_str(associations)}"].join("\n")
+ @@logger.info(log) if @@bullet_logger
+ Rails.logger.warn(log) if @@rails_logger
+ end
+ unpreload_associations.each do |klazz, associations|
+ log = ["N+1 Query in #{path}", klazz_associations_str(klazz, associations), " Add to your finder: #{associations_str(associations)}"].join("\n")
+ @@logger.info(log) if @@bullet_logger
+ Rails.logger.warn(log) if @@rails_logger
+ end
+ callers.each do |c|
+ log = ["N+1 Query method call stack", c.map{|line| " #{line}"}].flatten.join("\n")
+ @@logger.info(log) if @@bullet_logger
+ Rails.logger.warn(log) if @@rails_logger
+ end
+ @@logger_file.flush if @@bullet_logger
+ end
+ end
def bad_associations_str(bad_associations)
- puts bad_associations.inspect
+ # puts bad_associations.inspect
bad_associations.to_a.collect{|klazz, associations| klazz_associations_str(klazz, associations)}.join('\\n')
end
def klazz_associations_str(klazz, associations)
- "model: #{klazz} => associations: [#{associations.join(', ')}]"
+ " #{klazz} => [#{associations.map(&:inspect).join(', ')}]"
end
def associations_str(associations)
":include => #{associations.map{|a| a.to_sym unless a.is_a? Hash}.inspect}"
end
- def log_bad_associations(path)
- if @@logger
- unused_preload_associations.each do |klazz, associations|
- @@logger.info "Unused preload associations: PATH_INFO: #{path}; " + klazz_associations_str(klazz, associations) + "\n Remove from your finder: " + associations_str(associations)
- end
- unpreload_associations.each do |klazz, associations|
- @@logger.info "N+1 Query: PATH_INFO: #{path}; " + klazz_associations_str(klazz, associations) + "\n Add to your finder: " + associations_str(associations)
- end
- callers.each do |c|
- @@logger.info "N+1 Query: method call stack: \n" + c.join("\n")
- end
- @@logger_file.flush
- end
- end
-
def has_klazz_association(klazz)
!klazz_associations[klazz].nil? and klazz_associations.keys.include?(klazz)
end
def define_association(klazz, associations)
- # puts "define association, #{klazz} => #{associations}"
+ # puts "define association, #{klazz} => #{associations.inspect}"
add_klazz_associations(klazz, associations)
end
def call_association(object, associations)
- # puts "call association, #{object} => #{associations}"
+ # puts "call association, #{object} => #{associations.inspect}"
+ add_call_object_associations(object, associations)
if unpreload_associations?(object, associations)
add_unpreload_associations(object.class, associations)
- add_call_object_associations(object, associations)
caller_in_project
end
end
def unpreload_associations?(object, associations)
@@ -122,50 +181,59 @@
def add_unpreload_associations(klazz, associations)
# puts "add unpreload associations, #{klazz} => #{associations.inspect}"
unpreload_associations[klazz] ||= []
unpreload_associations[klazz] << associations
- unpreload_associations[klazz].uniq!
+ unique(unpreload_associations[klazz])
end
def add_unused_preload_associations(klazz, associations)
- # puts "add unused preload associations, #{object} => #{associations.inspect}"
+ # puts "add unused preload associations, #{klazz} => #{associations.inspect}"
unused_preload_associations[klazz] ||= []
unused_preload_associations[klazz] << associations
- unused_preload_associations[klazz].flatten!.uniq!
+ unique(unused_preload_associations[klazz])
end
def add_association(object, associations)
# puts "add associations, #{object} => #{associations.inspect}"
object_associations[object] ||= []
object_associations[object] << associations
+ unique(object_associations[object])
end
def add_call_object_associations(object, associations)
# puts "add call object associations, #{object} => #{associations.inspect}"
call_object_associations[object] ||= []
call_object_associations[object] << associations
+ unique(call_object_associations[object])
end
def add_possible_objects(objects)
# puts "add possible objects, #{objects.inspect}"
klazz= objects.first.class
possible_objects[klazz] ||= []
possible_objects[klazz] << objects
- possible_objects[klazz].flatten!.uniq!
+ unique(possible_objects[klazz])
end
def add_impossible_object(object)
# puts "add impossible object, #{object}"
klazz = object.class
impossible_objects[klazz] ||= []
impossible_objects[klazz] << object
+ impossible_objects[klazz].uniq!
end
def add_klazz_associations(klazz, associations)
# puts "define associations, #{klazz} => #{associations.inspect}"
klazz_associations[klazz] ||= []
klazz_associations[klazz] << associations
+ unique(klazz_associations[klazz])
+ end
+
+ def unique(array)
+ array.flatten!
+ array.uniq!
end
def unpreload_associations
@@unpreload_associations ||= {}
end