Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge remote branch 'nesq/master'

Conflicts:
	lib/query_reviewer/views/_box_body.html.erb
	lib/query_reviewer/views/_box_includes.rhtml
  • Loading branch information...
commit 8202e49a26710c8ef7a55288cce578a8663a04dd 2 parents 51ccc1f + 9de60e7
@dsboulder authored David Stevenson committed
Showing with 624 additions and 432 deletions.
  1. +3 −0  .gitignore
  2. +0 −51 README
  3. +122 −0 README.md
  4. +2 −0  Rakefile
  5. +1 −13 init.rb
  6. +25 −26 lib/query_reviewer.rb
  7. +12 −7 lib/query_reviewer/controller_extensions.rb
  8. +5 −5 lib/query_reviewer/mysql_analyzer.rb
  9. +37 −0 lib/query_reviewer/rails.rb
  10. +13 −13 lib/query_reviewer/sql_query.rb
  11. +2 −1  lib/query_reviewer/sql_sub_query.rb
  12. +8 −0 lib/query_reviewer/tasks.rb
  13. 0  lib/query_reviewer/views/{_box.rhtml → _box.html.erb}
  14. +3 −3 lib/query_reviewer/views/{_box_body.rhtml → _box_body.html.erb}
  15. 0  lib/query_reviewer/views/{_box_disabled.rhtml → _box_disabled.html.erb}
  16. 0  lib/query_reviewer/views/{_box_header.rhtml → _box_header.html.erb}
  17. +234 −0 lib/query_reviewer/views/_box_includes.html.erb
  18. +0 −297 lib/query_reviewer/views/_box_includes.rhtml
  19. 0  lib/query_reviewer/views/{_explain.rhtml → _explain.html.erb}
  20. +68 −0 lib/query_reviewer/views/_js_includes.html.erb
  21. +68 −0 lib/query_reviewer/views/_js_includes_new.html.erb
  22. 0  lib/query_reviewer/views/{_profile.rhtml → _profile.html.erb}
  23. 0  lib/query_reviewer/views/{_query_sql.rhtml → _query_sql.html.erb}
  24. +1 −1  lib/query_reviewer/views/{_query_trace.rhtml → _query_trace.html.erb}
  25. +3 −3 lib/query_reviewer/views/{_query_with_warning.rhtml → _query_with_warning.html.erb}
  26. 0  lib/query_reviewer/views/{_spectrum.rhtml → _spectrum.html.erb}
  27. 0  lib/query_reviewer/views/{_warning_no_query.rhtml → _warning_no_query.html.erb}
  28. +5 −4 lib/query_reviewer/views/query_review_box_helper.rb
  29. +10 −0 query_reviewer.gemspec
  30. +1 −0  rails/init.rb
  31. +0 −7 tasks/query_reviewer_tasks.rake
  32. +1 −1  test/test_helper.rb
View
3  .gitignore
@@ -0,0 +1,3 @@
+pkg/*
+*.gem
+.bundle
View
51 README
@@ -1,51 +0,0 @@
-QueryReviewer
-=============
-
-QueryReviewer is an advanced SQL query analyzer. It accomplishes the following goals:
-
- * View all EXPLAIN output for all SELECT queries to generate a page (and optionally SHOW PROFILE ALL)
- * Rate a page's SQL usage into one of three categories: OK, WARNING, CRITICAL
- * Attach meaningful warnings to individual queries, and collections of queries
- * Display interactive summary on page
-
-All you have to do is install it. You can optionally run:
- rake query_reviewer:setup
-
-Which will create config/query_reviewer.yml, see below for what these options mean. If you don't have a config file,
-the plugin will use the default in vendor/plugins/query_reviewer.
-
-
-Configuration
-=============
-
-The configuration file allows you to set configuration parameters shared across all rails environment, as well as
-overriding those shared parameteres with environment-specific parameters (such as disabling analysis on production!)
-
- * enabled: whether any output or query analysis is performed. Set this false in production!
- * inject_view: controls whether the output automatically is injected before the </body> in HTML output.
- * profiling: when enabled, runs the MySQL SET PROFILING=1 for queries longer than the warn_duration_threshold / 2.0
- * production_data: whether the duration of a query should be taken into account (if you don't have real data, don't let query duration effect you!)
-
- * stack_trace_lines: number of lines of call stack to include in the "short" version of the stack trace
- * trace_includes_vendor: whether the "short" verison of the stack trace should include files in /vendor
- * trace_includes_lib: whether the "short" verison of the stack trace should include files in /lib
-
- * warn_severity: the severity of problem that merits "WARNING" status
- * critical_severity: the severity of problem that merits "CRITICAL" status
-
- * warn_query_count: the number of queries in a single request that merits "WARNING" status
- * critical_query_count: the number of queries in a single request that merits "CRITICAL" status
-
- * warn_duration_threshold: how long a query must take in seconds (float) before it's considered "WARNING"
- * critical_duration_threshold: how long a query must take in seconds (float) before it's considered "CRITICIAL"
-
-
-Example
-=======
-
-If you disable the inject_view option, you'll need to manually put the analyzer's output into your view:
-
-<%= query_review_output %>
-
-
-Copyright (c) 2007-2008 Kongregate & David Stevenson, released under the MIT license
View
122 README.md
@@ -0,0 +1,122 @@
+# QueryReviewer #
+
+## Introduction ##
+
+QueryReviewer is an advanced SQL query analyzer. It accomplishes the following goals:
+
+ * View all EXPLAIN output for all SELECT queries to generate a page
+ * Rate a page's SQL usage into one of three categories: OK, WARNING, CRITICAL
+ * Attach meaningful warnings to individual queries, and collections of queries
+ * Display interactive summary on page
+
+## This Fork ##
+
+I use this utility for most of my rails projects. Still the best out there in my opinion for analyzing and understanding your generated SQL queries. I forked the original [query_reviewer](https://github.com/dsboulder/query_reviewer) and applied a collection of patches that have been made since the plugin was created. A list of the biggest additions below:
+
+ * Snazzed up the README into markdown for better readability
+ * Full compatibility for Rails 3 (including Railtie)
+ * Cleanup and move rake task to `lib/tasks` to fix deprecation warnings
+ * Added gemspec for use with Bundler (as a gem)
+ * Fixed missing tags and additional XHTML escaping
+ * Fix SQL escaping for better XHTML compatibility
+ * Fixes for deprecation warnings and for 1.9 compatiblity
+ * Converts templates to more modern foo.html.erb naming
+
+Last commit to the main repository was on March 30th, 2009. This fork compiles a variety of patches that were made since that time along with additional work to support compatibility with 1.9 and Rails 3. **Also:** If anyone else creates generally useful enhancements to this utility please start by forking this and then issue me a pull request.
+
+**Note:** This plugin should work for Rails 2.X and Rails 3. Support for Rails 3 has been confirmed in the latest revision (with fixed deprecation warnings).
+
+## Installation ##
+
+All you have to do is install it into your Rails 2 or 3 project.
+
+ gem install query_reviewer
+
+Right now if you use bundler, simply add this to your Gemfile:
+
+ # Gemfile
+ gem "query_reviewer", :git => "git://github.com/nesquena/query_reviewer.git"
+
+If you are not using bundler, you might want to [start using it](http://gembundler.com/rails23.html). You can also install this as a plugin:
+
+ script/plugin install git://github.com/nesquena/query_reviewer.git
+
+In Rails 2, the rake tasks are not loaded automatically (as a gem), you’ll need to add the following to your Rakefile:
+
+ # Rakefile
+ begin
+ require 'query_reviewer/tasks'
+ rescue LoadError
+ STDERR.puts "The query_reviewer gem could not be found!"
+ end
+
+You can then run:
+
+ $ rake query_reviewer:setup
+
+Which will create `config/query_reviewer.yml` in your application, see below for what these options mean.
+If you don't create a config file, the gem will use the default in `vendor/plugins/query_reviewer`.
+
+## Configuration ##
+
+The configuration file allows you to set configuration parameters shared across all rails environment, as well as overriding those shared parameteres with environment-specific parameters (such as disabling analysis on production!)
+
+ * `enabled`: whether any output or query analysis is performed. Set this false in production!
+ * `inject_view`: controls whether the output automatically is injected before the &lt;/body&gt; in HTML output.
+ * `profiling`: when enabled, runs the MySQL SET PROFILING=1 for queries longer than the `warn_duration_threshold` / 2.0
+ * `production_data`: whether the duration of a query should be taken into account
+ * `stack_trace_lines`: number of lines of call stack to include in the "short" version of the stack trace
+ * `trace_includes_vendor`: whether the "short" verison of the stack trace should include files in /vendor
+ * `trace_includes_lib`: whether the "short" verison of the stack trace should include files in /lib
+ * `warn_severity`: the severity of problem that merits "WARNING" status
+ * `critical_severity`: the severity of problem that merits "CRITICAL" status
+ * `warn_query_count`: the number of queries in a single request that merits "WARNING" status
+ * `critical_query_count`: the number of queries in a single request that merits "CRITICAL" status
+ * `warn_duration_threshold`: how long a query must take in seconds (float) before it's considered "WARNING"
+ * `critical_duration_threshold`: how long a query must take in seconds (float) before it's considered "CRITICIAL"
+
+## Example ##
+
+If you disable the inject_view option above, you'll need to manually put the analyzer's output into your view:
+
+ # view.html.haml
+ = query_review_output
+
+and that will display the analyzer view!
+
+## Resources ##
+
+Random collection of resources that might be interesting related to this utility:
+
+ * <http://blog.purifyapp.com/2010/06/15/optimise-your-mysql/>
+ * <http://www.tatvartha.com/2009/09/rails-optimizing-database-indexes-using-query_analyzer-and-query_reviewer/>
+ * <http://www.geekskillz.com/articles/using-indexes-to-improve-rails-performance>
+ * <http://www.williambharding.com/blog/rails/rails-mysql-indexes-step-1-in-pitiful-to-prime-performance/>
+ * <http://guides.rubyonrails.org/performance_testing.html>
+
+Other related gems that prove useful for database optimization:
+
+ * [bullet](https://github.com/flyerhzm/bullet)
+ * [slim-scrooge](https://github.com/sdsykes/slim_scrooge)
+ * [slim-attributes](https://github.com/sdsykes/slim-attributes)
+
+## Alternatives ##
+
+There have been other alternatives created since this was originally released. A few of the best are listed below. I for one still prefer this utility over the other options:
+
+ * [rack-bug](https://github.com/brynary/rack-bug)
+ * [rails-footnotes](https://github.com/josevalim/rails-footnotes)
+ * [newrelic-development](http://support.newrelic.com/kb/docs/developer-mode)
+ * [palmist](https://github.com/flyingmachine/palmist)
+ * [query_diet](https://github.com/makandra/query_diet)
+ * [query_trace](https://github.com/ntalbott/query_trace)
+
+Know of a better alternative? Let me know!
+
+## Acknowledgements ##
+
+Created by Kongregate & David Stevenson.
+Refactorings and compilations of all fixes since was done by Nathan Esquenazi.
+Also, ajvargo for helping with some fixes.
+
+Copyright (c) 2007-2008 Kongregate & David Stevenson, released under the MIT license
View
2  Rakefile
@@ -1,6 +1,8 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'
+require 'bundler'
+Bundler::GemHelper.install_tasks
desc 'Default: run unit tests.'
task :default => :test
View
14 init.rb
@@ -1,13 +1 @@
-# Include hook code here
-
-require 'query_reviewer'
-
-if QueryReviewer.enabled?
- ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:include, QueryReviewer::MysqlAdapterExtensions)
- ActionController::Base.send(:include, QueryReviewer::ControllerExtensions)
- Array.send(:include, QueryReviewer::ArrayExtensions)
-
- if ActionController::Base.respond_to?(:append_view_path)
- ActionController::Base.append_view_path(File.dirname(__FILE__) + "/lib/query_reviewer/views")
- end
-end
+require File.dirname(__FILE__) + "/rails/init"
View
51 lib/query_reviewer.rb
@@ -5,47 +5,46 @@
module QueryReviewer
CONFIGURATION = {}
-
+
def self.load_configuration
default_config = YAML::load(ERB.new(IO.read(File.join(File.dirname(__FILE__), "..", "query_reviewer_defaults.yml"))).result)
-
+
CONFIGURATION.merge!(default_config["all"] || {})
- CONFIGURATION.merge!(default_config[RAILS_ENV || "test"] || {})
-
- app_config_file = File.join(RAILS_ROOT, "config", "query_reviewer.yml")
-
- if File.exist?(app_config_file)
+ CONFIGURATION.merge!(default_config[Rails.env || "test"] || {})
+
+ app_config_file = Rails.root + "config/query_reviewer.yml"
+
+ if app_config_file.exist?
app_config = YAML.load(ERB.new(IO.read(app_config_file)).result)
- CONFIGURATION.merge!(app_config["all"] || {})
- CONFIGURATION.merge!(app_config[RAILS_ENV || "test"] || {})
+ CONFIGURATION.merge!(app_config["all"] || {})
+ CONFIGURATION.merge!(app_config[Rails.env || "test"] || {})
end
-
+
if enabled?
- begin
+ begin
CONFIGURATION["uv"] ||= !Gem.searcher.find("uv").nil?
if CONFIGURATION["uv"]
require "uv"
end
rescue
- CONFIGURATION["uv"] ||= false
+ CONFIGURATION["uv"] ||= false
end
- end
+
+ require "query_reviewer/query_warning"
+ require "query_reviewer/array_extensions"
+ require "query_reviewer/sql_query"
+ require "query_reviewer/mysql_analyzer"
+ require "query_reviewer/sql_sub_query"
+ require "query_reviewer/mysql_adapter_extensions"
+ require "query_reviewer/controller_extensions"
+ require "query_reviewer/sql_query_collection"
+ end
end
-
+
def self.enabled?
CONFIGURATION["enabled"]
end
end
-QueryReviewer.load_configuration
-
-if QueryReviewer.enabled?
- require "query_reviewer/query_warning"
- require "query_reviewer/array_extensions"
- require "query_reviewer/sql_query"
- require "query_reviewer/mysql_analyzer"
- require "query_reviewer/sql_sub_query"
- require "query_reviewer/mysql_adapter_extensions"
- require "query_reviewer/controller_extensions"
- require "query_reviewer/sql_query_collection"
-end
+# Rails Integration
+require 'query_reviewer/rails' if defined?(Rails)
View
19 lib/query_reviewer/controller_extensions.rb
@@ -1,3 +1,4 @@
+require "action_view"
require File.join(File.dirname(__FILE__), "views", "query_review_box_helper")
module QueryReviewer
@@ -7,7 +8,10 @@ class QueryViewBase < ActionView::Base
end
def self.included(base)
- base.alias_method_chain :perform_action, :query_review if QueryReviewer::CONFIGURATION["inject_view"]
+ if QueryReviewer::CONFIGURATION["inject_view"]
+ alias_name = defined?(Rails::Railtie) ? :process_action : :perform_action
+ base.alias_method_chain(alias_name, :query_review)
+ end
base.alias_method_chain :process, :query_review
base.helper_method :query_review_output
end
@@ -38,23 +42,24 @@ def add_query_output_to_view(total_time)
if response.body.is_a?(String) && response.body.match(/<\/body>/i) && Thread.current["queries"]
idx = (response.body =~ /<\/body>/i)
html = query_review_output(false, total_time)
- response.body.insert(idx, html)
+ response.body = response.body.insert(idx, html)
end
end
end
- def perform_action_with_query_review
+ def perform_action_with_query_review(*args)
Thread.current["query_reviewer_enabled"] = cookies["query_review_enabled"]
t1 = Time.now
- r = perform_action_without_query_review
+ r = defined?(Rails::Railtie) ? process_action_without_query_review(*args) : perform_action_without_query_review(*args)
t2 = Time.now
add_query_output_to_view(t2 - t1)
r
end
+ alias_method :process_action_with_query_review, :perform_action_with_query_review
- def process_with_query_review(request, response, method = :perform_action, *arguments) #:nodoc:
+ def process_with_query_review(*args) #:nodoc:
Thread.current["queries"] = SqlQueryCollection.new
- process_without_query_review(request, response, method, *arguments)
+ process_without_query_review(*args)
end
end
-end
+end
View
10 lib/query_reviewer/mysql_analyzer.rb
@@ -18,15 +18,15 @@ def analyze_select_type!
def analyze_query_type!
case query_type
- when "system", "const", "eq_ref":
+ when "system", "const", "eq_ref" then
praise("Yay")
- when "ref", "ref_or_null", "range", "index_merge":
+ when "ref", "ref_or_null", "range", "index_merge" then
praise("Not bad eh...")
- when "unique_subquery", "index_subquery":
+ when "unique_subquery", "index_subquery" then
#NOT SURE
- when "index":
+ when "index" then
warn(:severity => 8, :field => "query_type", :desc => "Full index tree scan (slightly faster than a full table scan)") unless !extra.include?("using where")
- when "all":
+ when "all" then
warn(:severity => 9, :field => "query_type", :desc => "Full table scan") unless !extra.include?("using where")
end
end
View
37 lib/query_reviewer/rails.rb
@@ -0,0 +1,37 @@
+require 'query_reviewer'
+
+module QueryReviewer
+ def self.inject_reviewer
+ # Load adapters
+ ActiveRecord::Base
+ adapter_class = ActiveRecord::ConnectionAdapters::MysqlAdapter if defined? ActiveRecord::ConnectionAdapters::MysqlAdapter
+ adapter_class = ActiveRecord::ConnectionAdapters::Mysql2Adapter if defined? ActiveRecord::ConnectionAdapters::Mysql2Adapter
+ adapter_class.send(:include, QueryReviewer::MysqlAdapterExtensions) if adapter_class
+ # Load into controllers
+ ActionController::Base.send(:include, QueryReviewer::ControllerExtensions)
+ Array.send(:include, QueryReviewer::ArrayExtensions)
+ if ActionController::Base.respond_to?(:append_view_path)
+ ActionController::Base.append_view_path(File.dirname(__FILE__) + "/lib/query_reviewer/views")
+ end
+ end
+end
+
+if defined?(Rails::Railtie)
+ module QueryReviewer
+ class Railtie < Rails::Railtie
+ rake_tasks do
+ load File.dirname(__FILE__) + "/tasks.rb"
+ end
+
+ initializer "query_reviewer.initialize" do
+ QueryReviewer.load_configuration
+
+ QueryReviewer.inject_reviewer if QueryReviewer.enabled?
+ end
+ end
+ end
+else # Rails 2
+ QueryReviewer.load_configuration
+
+ QueryReviewer.inject_reviewer
+end
View
26 lib/query_reviewer/sql_query.rb
@@ -21,25 +21,25 @@ def initialize(sql, rows, full_trace, duration = 0.0, profile = nil, command = "
@command = command
@affected_rows = affected_rows
end
-
+
def add(sql, duration, profile)
sql << sql
durations << duration
profiles << profile
end
-
+
def sql
sqls.first
end
-
+
def count
durations.size
end
-
+
def profile
profiles.first
end
-
+
def duration
durations.sum
end
@@ -77,7 +77,7 @@ def analyze!
warn(:problem => "Query took #{duration} seconds", :severity => QueryReviewer::CONFIGURATION["critical_severity"])
end
end
-
+
if affected_rows >= QueryReviewer::CONFIGURATION["critical_affected_rows"]
warn(:problem => "#{affected_rows} rows affected", :severity => 9, :description => "An UPDATE or DELETE query can be slow and lock tables if it affects many rows.")
elsif affected_rows >= QueryReviewer::CONFIGURATION["warn_affected_rows"]
@@ -90,10 +90,10 @@ def to_hash
end
def relevant_trace
- trace.collect(&:strip).select{|t| t.starts_with?(RAILS_ROOT) &&
- (!t.starts_with?("#{RAILS_ROOT}/vendor") || QueryReviewer::CONFIGURATION["trace_includes_vendor"]) &&
- (!t.starts_with?("#{RAILS_ROOT}/lib") || QueryReviewer::CONFIGURATION["trace_includes_lib"]) &&
- !t.starts_with?("#{RAILS_ROOT}/vendor/plugins/query_reviewer") }
+ trace.collect(&:strip).select{|t| t.starts_with?(Rails.root.to_s) &&
+ (!t.starts_with?("#{Rails.root}/vendor") || QueryReviewer::CONFIGURATION["trace_includes_vendor"]) &&
+ (!t.starts_with?("#{Rails.root}/lib") || QueryReviewer::CONFIGURATION["trace_includes_lib"]) &&
+ !t.starts_with?("#{Rails.root}/vendor/plugins/query_reviewer") }
end
def full_trace
@@ -109,11 +109,11 @@ def warn(options)
def select?
self.command == "SELECT"
end
-
+
def self.generate_full_trace(trace = Kernel.caller)
- trace.collect(&:strip).select{|t| !t.starts_with?("#{RAILS_ROOT}/vendor/plugins/query_reviewer") }
+ trace.collect(&:strip).select{|t| !t.starts_with?("#{Rails.root}/vendor/plugins/query_reviewer") }
end
-
+
def self.sanitize_strings_and_numbers_from_sql(sql)
new_sql = sql.clone
new_sql.gsub!(/\b\d+\b/, "N")
View
3  lib/query_reviewer/sql_sub_query.rb
@@ -16,6 +16,7 @@ def initialize(parent, cols)
def analyze!
@warnings = []
adapter_name = ActiveRecord::Base.connection.instance_variable_get("@config")[:adapter]
+ adapter_name = 'mysql' if adapter_name == 'mysql2'
method_name = "do_#{adapter_name}_analysis!"
self.send(method_name.to_sym)
end
@@ -41,4 +42,4 @@ def praise(options)
# no credit, only pain
end
end
-end
+end
View
8 lib/query_reviewer/tasks.rb
@@ -0,0 +1,8 @@
+namespace :query_reviewer do
+ desc "Create a default config/query_reviewer.yml"
+ task :setup do
+ defaults_path = File.join(File.dirname(__FILE__), "../..", "query_reviewer_defaults.yml")
+ dest_path = File.join(Rails.root.to_s, "config", "query_reviewer.yml")
+ FileUtils.copy(defaults_path, dest_path)
+ end
+end
View
0  lib/query_reviewer/views/_box.rhtml → lib/query_reviewer/views/_box.html.erb
File renamed without changes
View
6 lib/query_reviewer/views/_box_body.rhtml → lib/query_reviewer/views/_box_body.html.erb
@@ -46,7 +46,7 @@
<%= query.count %> identical queries
</b>
<% end %>
- <%= render :partial => "/query_sql", :object => query %>
+ <%= render :partial => "/query_sql", :locals=>{ :query_sql => query } %>
<% if query.select? %>
<a href="javascript: query_review_toggle('warning_<%= query.id %>_explain')" title="show/hide sql">EXPLN</a>
<% end %>
@@ -63,11 +63,11 @@
</div>
<% end %>
<div style="display: none" id="warning_<%= query.id %>_trace" class="indent small">
- <%= render :partial => "/query_trace", :object => query.relevant_trace, :locals => {:query_id => query.id, :full_trace => query.full_trace} %>
+ <%= render :partial => "/query_trace", :locals => {:query_trace => query.relevant_trace, :query_id => query.id, :full_trace => query.full_trace} %>
</div>
</li>
<% end %>
</ul>
<% end %>
<% end %>
- <p id="query_review_disable_link"><a href="javascript: eraseCookie('query_review_enabled'); query_review_hide('query_review_disable_link'); alert('Cookie successfully set.');">Disable analysis report</a> on next page load and from now on.</p>
+ <p id="query_review_disable_link"><a href="javascript: eraseCookie('query_review_enabled'); query_review_hide('query_review_disable_link'); alert('Cookie successfully set.');">Disable analysis report</a> on next page load and from now on.</p>
View
0  lib/query_reviewer/views/_box_disabled.rhtml → ...ery_reviewer/views/_box_disabled.html.erb
File renamed without changes
View
0  lib/query_reviewer/views/_box_header.rhtml → ...query_reviewer/views/_box_header.html.erb
File renamed without changes
View
234 lib/query_reviewer/views/_box_includes.html.erb
@@ -0,0 +1,234 @@
+<style>
+div#query_review_parent {
+ position: absolute;
+ top: 1px;
+ left: 1px;
+ z-index: 100000;
+}
+
+div.query_review_container {
+ float: left;
+}
+
+div.query_review {
+ height: 18px;
+ margin: 1px;
+ opacity: 0.75;
+ padding-left: 3px;
+ padding-right: 3px;
+ border: 1px solid black;
+ font-size: 12px;
+ font-weight: bold;
+ text-align: left;
+}
+
+div.query_review.sql_ok {
+ background-color: #090;
+ color: #020;
+}
+
+div.query_review.sql_warning {
+ background-color: #FAE100;
+ color: #F05000;
+}
+
+div.query_review.sql_critical {
+ background-color: #F99;
+ color: #A00;
+}
+
+div.query_review.sql_disabled {
+ background-color: #BBB;
+ color: #555;
+}
+
+div.query_review a {
+ color: inherit;
+ text-decoration: none;
+}
+
+div.query_review_details {
+ width: 900px;
+ height: 600px;
+ opacity: 0.9;
+ margin: 1px;
+ padding: 2px;
+ border: 1px solid black;
+ font-size: 12px;
+ overflow: auto;
+ background-color: #DDD;
+ text-align: left;
+ text-indent: 0px;
+ color: black;
+}
+
+div.query_review_details p {
+ margin: 2px 0 2px 0;
+ padding: 0px;
+}
+
+div.query_review_details a {
+ color: #29ABE2;
+}
+
+div.query_review_details ul {
+ list-style-type: circle;
+ padding-left: 15px;
+}
+
+div.query_review_details li {
+ clear: both;
+}
+
+div.query_review_details code {
+ white-space: normal;
+ line-height: 120%;
+}
+
+div.query_review_details table.explain th {
+ cell-padding: 5px;
+}
+
+div.query_review_details .title {
+ font-weight: bold;
+}
+
+div.query_review_details .indent {
+ padding-left: 10px;
+}
+
+div.query_review_details .number {
+ font-weight: bold;
+}
+
+div.query_review_details .bad {
+ color: #900;
+}
+
+div.query_review_details .good {
+ color: #090;
+}
+
+div.query_review_details div.divider {
+ width: 504px;
+ position: relative;
+ left: -2px;
+ height: 1px;
+ border-top: 1px dashed black;
+ margin: 2px 0 2px 0;
+}
+
+div.query_review_details .small {
+ font-size: 10px;
+}
+
+div.query_review_details .tbpadded {
+ padding-top: 3px;
+ padding-bottom: 3px;
+}
+
+div.query_review_details .trace {
+ background-color: #0C1021;
+ color: #7F908A;
+ padding: 5px;
+ margin-right: 30px;
+}
+
+div.query_review_details .trace .bold {
+ color: white;
+}
+
+div.query_review_details div.spectrum_container {
+ margin-top: 4px;
+ width: 35px;
+ display: block;
+ float: left;
+ position: relative;
+ height: 14px;
+ margin-right: 5px;
+}
+
+div.query_review_details div.spectrum_elem {
+ width: 1px;
+ display: block;
+ float: left;
+ height: 10px;
+}
+div.query_review_details div.spectrum_pointer {
+ width: 2px;
+ display: block;
+ float: left;
+ position: absolute;
+ height: 14px;
+ background-color: black;
+ top: -2px;
+}
+
+div.query_review_details table {
+ background-color: white;
+ border: 1px solid gray;
+ padding: 0px;
+ margin: 0px;
+}
+
+div.query_review_details tbody {
+ border: 0px;
+ padding: 0px;
+ margin: 0px;
+}
+
+div.query_review_details thead {
+ border-bottom: 1px solid gray;
+ padding: 0px;
+ margin: 0px;
+}
+
+
+div.query_review_details tr {
+ border: 0px;
+ padding: 0px;
+ margin: 0px;
+}
+
+div.query_review_details td {
+ border: 1px solid gray;
+ padding: 3px;
+ margin: 0px;
+ overflow: hidden;
+}
+
+div.query_review_details th {
+ border: 1px solid gray;
+ padding: 3px;
+ margin: 0px;
+}
+
+</style>
+
+<style>
+.sql {
+ color: black;
+}
+.sql .String {
+ color: #009933;
+}
+.sql .Keyword {
+ color: #0000FF;
+}
+.sql .Constant {
+ color: #6782D3;
+}
+.sql .Number {
+ color: #0066FF;
+}
+.sql .Comment {
+ color: #0066FF;
+ font-style: italic;
+}
+</style>
+
+<% if defined?(Rails::Railtie) %>
+ <%= render :partial => "/js_includes_new" %>
+<% else %>
+ <%= render :partial => "/js_includes" %>
+<% end %>
View
297 lib/query_reviewer/views/_box_includes.rhtml
@@ -1,297 +0,0 @@
-<style>
-div#query_review_parent {
- position: absolute;
- top: 1px;
- left: 1px;
- z-index: 100000;
-}
-
-div.query_review_container {
- float: left;
-}
-
-div.query_review {
- height: 18px;
- margin: 1px;
- opacity: 0.75;
- padding-left: 3px;
- padding-right: 3px;
- border: 1px solid black;
- font-size: 12px;
- font-weight: bold;
- text-align: left;
-}
-
-div.query_review.sql_ok {
- background-color: #090;
- color: #020;
-}
-
-div.query_review.sql_warning {
- background-color: #FAE100;
- color: #F05000;
-}
-
-div.query_review.sql_critical {
- background-color: #F99;
- color: #A00;
-}
-
-div.query_review.sql_disabled {
- background-color: #BBB;
- color: #555;
-}
-
-div.query_review a {
- color: inherit;
- text-decoration: none;
-}
-
-div.query_review_details {
- width: 900px;
- height: 600px;
- opacity: 0.9;
- margin: 1px;
- padding: 2px;
- border: 1px solid black;
- font-size: 12px;
- overflow: auto;
- background-color: #DDD;
- text-align: left;
- text-indent: 0px;
- color: black;
-}
-
-div.query_review_details p {
- margin: 2px 0 2px 0;
- padding: 0px;
-}
-
-div.query_review_details a {
- color: #29ABE2;
-}
-
-div.query_review_details ul {
- list-style-type: circle;
- padding-left: 15px;
-}
-
-div.query_review_details li {
- clear: both;
-}
-
-div.query_review_details code {
- white-space: normal;
- line-height: 120%;
-}
-
-div.query_review_details table.explain th {
- cell-padding: 5px;
-}
-
-div.query_review_details .title {
- font-weight: bold;
-}
-
-div.query_review_details .indent {
- padding-left: 10px;
-}
-
-div.query_review_details .number {
- font-weight: bold;
-}
-
-div.query_review_details .bad {
- color: #900;
-}
-
-div.query_review_details .good {
- color: #090;
-}
-
-div.query_review_details div.divider {
- width: 504px;
- position: relative;
- left: -2px;
- height: 1px;
- border-top: 1px dashed black;
- margin: 2px 0 2px 0;
-}
-
-div.query_review_details .small {
- font-size: 10px;
-}
-
-div.query_review_details .tbpadded {
- padding-top: 3px;
- padding-bottom: 3px;
-}
-
-div.query_review_details .trace {
- background-color: #0C1021;
- color: #7F908A;
- padding: 5px;
- margin-right: 30px;
-}
-
-div.query_review_details .trace .bold {
- color: white;
-}
-
-div.query_review_details div.spectrum_container {
- margin-top: 4px;
- width: 35px;
- display: block;
- float: left;
- position: relative;
- height: 14px;
- margin-right: 5px;
-}
-
-div.query_review_details div.spectrum_elem {
- width: 1px;
- display: block;
- float: left;
- height: 10px;
-}
-div.query_review_details div.spectrum_pointer {
- width: 2px;
- display: block;
- float: left;
- position: absolute;
- height: 14px;
- background-color: black;
- top: -2px;
-}
-
-div.query_review_details table {
- background-color: white;
- border: 1px solid gray;
- padding: 0px;
- margin: 0px;
-}
-
-div.query_review_details tbody {
- border: 0px;
- padding: 0px;
- margin: 0px;
-}
-
-div.query_review_details thead {
- border-bottom: 1px solid gray;
- padding: 0px;
- margin: 0px;
-}
-
-
-div.query_review_details tr {
- border: 0px;
- padding: 0px;
- margin: 0px;
-}
-
-div.query_review_details td {
- border: 1px solid gray;
- padding: 3px;
- margin: 0px;
- overflow: hidden;
-}
-
-div.query_review_details th {
- border: 1px solid gray;
- padding: 3px;
- margin: 0px;
-}
-
-</style>
-
-<style>
-.sql {
- color: black;
-}
-.sql .String {
- color: #009933;
-}
-.sql .Keyword {
- color: #0000FF;
-}
-.sql .Constant {
- color: #6782D3;
-}
-.sql .Number {
- color: #0066FF;
-}
-.sql .Comment {
- color: #0066FF;
- font-style: italic;
-}
-</style>
-
-<% javascript_tag do %>
-//Super lame show/hide functions so we don't need any 3rd party JS libs
-function query_review_show(id) {
- document.getElementById(id).style.display = "block";
-}
-
-function query_review_hide(id) {
- document.getElementById(id).style.display = "none";
-}
-function query_review_toggle(id) {
- if(document.getElementById(id).style.display == "none") {
- document.getElementById(id).style.display = "block";
- } else {
- document.getElementById(id).style.display = "none";
- }
-}
-function createCookie(name,value,days) {
- if (days) {
- var date = new Date();
- date.setTime(date.getTime()+(days*24*60*60*1000));
- var expires = "; expires="+date.toGMTString();
- }
- else var expires = "";
- document.cookie = name+"="+value+expires+"; path=/";
-}
-
-function readCookie(name) {
- var nameEQ = name + "=";
- var ca = document.cookie.split(';');
- for(var i=0;i < ca.length;i++) {
- var c = ca[i];
- while (c.charAt(0)==' ') c = c.substring(1,c.length);
- if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
- }
- return null;
-}
-
-function eraseCookie(name) {
- createCookie(name,"",-1);
-}
-
-function ignore_list() {
- var ignore_list = readCookie("query_review_ignore_list")
- if (!ignore_list)
- ignore_list = []
- else
- ignore_list = ignore_list.split(",")
- return ignore_list
-}
-
-function add_ignore_hash(h){
- var list = ignore_list();
- list[list.length] = h
- createCookie("query_review_ignore_list", list.join(","))
-}
-
-function remove_ignore_hash(h) {
- var list = ignore_list();
- var new_list = []
- for(var i=0; i<list.length; i++)
- {
- if(list[i].toString() != h.toString()) {
- new_list[new_list.length] = list[i]
- }
- }
- createCookie("query_review_ignore_list", new_list.join(","))
-}
-<% end %>
View
0  lib/query_reviewer/views/_explain.rhtml → lib/query_reviewer/views/_explain.html.erb
File renamed without changes
View
68 lib/query_reviewer/views/_js_includes.html.erb
@@ -0,0 +1,68 @@
+<% javascript_tag do %>
+//Super lame show/hide functions so we don't need any 3rd party JS libs
+function query_review_show(id) {
+ document.getElementById(id).style.display = "block";
+}
+
+function query_review_hide(id) {
+ document.getElementById(id).style.display = "none";
+}
+function query_review_toggle(id) {
+ if(document.getElementById(id).style.display == "none") {
+ document.getElementById(id).style.display = "block";
+ } else {
+ document.getElementById(id).style.display = "none";
+ }
+}
+function createCookie(name,value,days) {
+ if (days) {
+ var date = new Date();
+ date.setTime(date.getTime()+(days*24*60*60*1000));
+ var expires = "; expires="+date.toGMTString();
+ }
+ else var expires = "";
+ document.cookie = name+"="+value+expires+"; path=/";
+}
+
+function readCookie(name) {
+ var nameEQ = name + "=";
+ var ca = document.cookie.split(';');
+ for(var i=0;i < ca.length;i++) {
+ var c = ca[i];
+ while (c.charAt(0)==' ') c = c.substring(1,c.length);
+ if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
+ }
+ return null;
+}
+
+function eraseCookie(name) {
+ createCookie(name,"",-1);
+}
+
+function ignore_list() {
+ var ignore_list = readCookie("query_review_ignore_list")
+ if (!ignore_list)
+ ignore_list = []
+ else
+ ignore_list = ignore_list.split(",")
+ return ignore_list
+}
+
+function add_ignore_hash(h){
+ var list = ignore_list();
+ list[list.length] = h
+ createCookie("query_review_ignore_list", list.join(","))
+}
+
+function remove_ignore_hash(h) {
+ var list = ignore_list();
+ var new_list = []
+ for(var i=0; i<list.length; i++)
+ {
+ if(list[i].toString() != h.toString()) {
+ new_list[new_list.length] = list[i]
+ }
+ }
+ createCookie("query_review_ignore_list", new_list.join(","))
+}
+<% end %>
View
68 lib/query_reviewer/views/_js_includes_new.html.erb
@@ -0,0 +1,68 @@
+<%= javascript_tag do %>
+//Super lame show/hide functions so we don't need any 3rd party JS libs
+function query_review_show(id) {
+ document.getElementById(id).style.display = "block";
+}
+
+function query_review_hide(id) {
+ document.getElementById(id).style.display = "none";
+}
+function query_review_toggle(id) {
+ if(document.getElementById(id).style.display == "none") {
+ document.getElementById(id).style.display = "block";
+ } else {
+ document.getElementById(id).style.display = "none";
+ }
+}
+function createCookie(name,value,days) {
+ if (days) {
+ var date = new Date();
+ date.setTime(date.getTime()+(days*24*60*60*1000));
+ var expires = "; expires="+date.toGMTString();
+ }
+ else var expires = "";
+ document.cookie = name+"="+value+expires+"; path=/";
+}
+
+function readCookie(name) {
+ var nameEQ = name + "=";
+ var ca = document.cookie.split(';');
+ for(var i=0;i < ca.length;i++) {
+ var c = ca[i];
+ while (c.charAt(0)==' ') c = c.substring(1,c.length);
+ if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
+ }
+ return null;
+}
+
+function eraseCookie(name) {
+ createCookie(name,"",-1);
+}
+
+function ignore_list() {
+ var ignore_list = readCookie("query_review_ignore_list")
+ if (!ignore_list)
+ ignore_list = []
+ else
+ ignore_list = ignore_list.split(",")
+ return ignore_list
+}
+
+function add_ignore_hash(h){
+ var list = ignore_list();
+ list[list.length] = h
+ createCookie("query_review_ignore_list", list.join(","))
+}
+
+function remove_ignore_hash(h) {
+ var list = ignore_list();
+ var new_list = []
+ for(var i=0; i<list.length; i++)
+ {
+ if(list[i].toString() != h.toString()) {
+ new_list[new_list.length] = list[i]
+ }
+ }
+ createCookie("query_review_ignore_list", new_list.join(","))
+}
+<% end %>
View
0  lib/query_reviewer/views/_profile.rhtml → lib/query_reviewer/views/_profile.html.erb
File renamed without changes
View
0  lib/query_reviewer/views/_query_sql.rhtml → lib/query_reviewer/views/_query_sql.html.erb
File renamed without changes
View
2  lib/query_reviewer/views/_query_trace.rhtml → ...uery_reviewer/views/_query_trace.html.erb
@@ -28,4 +28,4 @@
<% end %>
</code>
<a href="javascript: query_review_toggle('trace_<%= query_id %>_abridged'); query_review_toggle('trace_<%= query_id %>_full')" title="show short trace">SHORT</a>
-</div>
+</div>
View
6 ..._reviewer/views/_query_with_warning.rhtml → ...viewer/views/_query_with_warning.html.erb
@@ -38,7 +38,7 @@
<% end %>
</p>
<p style="display: none" id="warning_<%= query_with_warning.id %>_sql" class="indent small tbpadded">
- <%= render :partial => "/query_sql", :object => query_with_warning %>
+ <%= render :partial => "/query_sql", :locals => {:query_sql => query_with_warning} %>
</p>
<div style="display: none" id="warning_<%= query_with_warning.id %>_explain" class="indent small tbpadded">
<%= render :partial => "/explain", :locals => {:query => query_with_warning} %>
@@ -49,6 +49,6 @@
</div>
<% end %>
<div style="display: none" id="warning_<%= query_with_warning.id %>_trace" class="indent small">
- <%= render :partial => "/query_trace", :object => query_with_warning.relevant_trace, :locals => {:query_id => query_with_warning.id, :full_trace => query_with_warning.full_trace} %>
+ <%= render :partial => "/query_trace", :locals => {:query_id => query_with_warning.id, :full_trace => query_with_warning.full_trace,:query_trace => query_with_warning.relevant_trace} %>
</div>
-</li>
+</li>
View
0  lib/query_reviewer/views/_spectrum.rhtml → lib/query_reviewer/views/_spectrum.html.erb
File renamed without changes
View
0  ...ry_reviewer/views/_warning_no_query.rhtml → ...reviewer/views/_warning_no_query.html.erb
File renamed without changes
View
9 lib/query_reviewer/views/query_review_box_helper.rb
@@ -24,7 +24,7 @@ def syntax_highlighted_sql(sql)
uv_out = Uv.parse(sql, "xhtml", "sql_rails", false, "blackboard")
uv_out.gsub("<pre class=\"blackboard\">", "<code class=\"sql\">").gsub("</pre>", "</code>")
else
- sql
+ sql.gsub(/</, "&lt;").gsub(/>/, "&gt;")
end
end
@@ -46,7 +46,7 @@ def severity_color(severity)
end
def ignore_hash?(h)
- (@controller.send(:cookies)["query_review_ignore_list"] || "").split(",").include?(h.to_s)
+ (controller.send(:cookies)["query_review_ignore_list"] || "").split(",").include?(h.to_s)
end
def queries_with_warnings
@@ -78,19 +78,20 @@ def warnings_no_query_sorted_nonignored
end
def enabled_by_cookie
- @controller.send(:cookies)["query_review_enabled"]
+ controller.send(:cookies)["query_review_enabled"]
end
def duration_with_color(query)
title = query.duration_stats
duration = query.duration
- if duration > QueryReviewer::CONFIGURATION["critical_duration_threshold"]
+ span_html = if duration > QueryReviewer::CONFIGURATION["critical_duration_threshold"]
"<span style=\"color: #{severity_color(9)}\" title=\"#{title}\">#{"%.3f" % duration}</span>"
elsif duration > QueryReviewer::CONFIGURATION["warn_duration_threshold"]
"<span style=\"color: #{severity_color(QueryReviewer::CONFIGURATION["critical_severity"])}\" title=\"#{title}\">#{"%.3f" % duration}</span>"
else
"<span title=\"#{title}\">#{"%.3f" % duration}</span>"
end
+ span_html.respond_to?(:html_safe) ? span_html.html_safe : span_html
end
end
end
View
10 query_reviewer.gemspec
@@ -0,0 +1,10 @@
+Gem::Specification.new do |s|
+ s.name = 'query_reviewer'
+ s.version = '0.1.2'
+ s.author = 'dsboulder, nesquena'
+ s.email = 'nesquena' + '@' + 'gmail.com'
+ s.homepage = 'https://github.com/nesquena/query_reviewer'
+ s.summary = 'Runs explain before each select query and displays results in an overlayed div'
+ s.description = s.summary
+ s.files = %w[MIT-LICENSE Rakefile README.md query_reviewer_defaults.yml] + Dir["lib/**/*"]
+end
View
1  rails/init.rb
@@ -0,0 +1 @@
+require 'query_reviewer'
View
7 tasks/query_reviewer_tasks.rake
@@ -1,7 +0,0 @@
-# desc "Explaining what the task does"
-namespace :query_reviewer do
- desc "Create a default config/query_reviewer.yml"
- task :setup do
- FileUtils.copy(File.join(File.dirname(__FILE__), "..", "query_reviewer_defaults.yml"), File.join(RAILS_ROOT, "config", "query_reviewer.yml"))
- end
-end
View
2  test/test_helper.rb
@@ -1,5 +1,5 @@
require "rubygems"
-require "activesupport"
+require "active_support"
require 'test/unit'
require "query_reviewer"
Please sign in to comment.
Something went wrong with that request. Please try again.