rdavila-query_reviewer 0.1.0

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 (34) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README +51 -0
  3. data/Rakefile +22 -0
  4. data/init.rb +13 -0
  5. data/lib/query_reviewer.rb +51 -0
  6. data/lib/query_reviewer/array_extensions.rb +29 -0
  7. data/lib/query_reviewer/controller_extensions.rb +60 -0
  8. data/lib/query_reviewer/mysql_adapter_extensions.rb +90 -0
  9. data/lib/query_reviewer/mysql_analyzer.rb +62 -0
  10. data/lib/query_reviewer/query_warning.rb +17 -0
  11. data/lib/query_reviewer/sql_query.rb +130 -0
  12. data/lib/query_reviewer/sql_query_collection.rb +103 -0
  13. data/lib/query_reviewer/sql_sub_query.rb +44 -0
  14. data/lib/query_reviewer/views/_box.rhtml +11 -0
  15. data/lib/query_reviewer/views/_box_ajax.js +34 -0
  16. data/lib/query_reviewer/views/_box_body.rhtml +73 -0
  17. data/lib/query_reviewer/views/_box_disabled.rhtml +2 -0
  18. data/lib/query_reviewer/views/_box_header.rhtml +1 -0
  19. data/lib/query_reviewer/views/_box_includes.rhtml +297 -0
  20. data/lib/query_reviewer/views/_explain.rhtml +30 -0
  21. data/lib/query_reviewer/views/_profile.rhtml +26 -0
  22. data/lib/query_reviewer/views/_query_sql.rhtml +8 -0
  23. data/lib/query_reviewer/views/_query_trace.rhtml +31 -0
  24. data/lib/query_reviewer/views/_query_with_warning.rhtml +54 -0
  25. data/lib/query_reviewer/views/_spectrum.rhtml +10 -0
  26. data/lib/query_reviewer/views/_warning_no_query.rhtml +8 -0
  27. data/lib/query_reviewer/views/query_review_box_helper.rb +97 -0
  28. data/query_reviewer_defaults.yml +39 -0
  29. data/rails/init.rb +13 -0
  30. data/tasks/query_reviewer_tasks.rake +7 -0
  31. data/test/sql_query_test.rb +8 -0
  32. data/test/sql_sub_query_test.rb +17 -0
  33. data/test/test_helper.rb +18 -0
  34. metadata +97 -0
@@ -0,0 +1,30 @@
1
+ <table class="explain">
2
+ <thead>
3
+ <tr>
4
+ <th>table</th>
5
+ <th>select_type</th>
6
+ <th>type</th>
7
+ <th>extra</th>
8
+ <th>possible_keys</th>
9
+ <th>key</th>
10
+ <th>key length</th>
11
+ <th>ref</th>
12
+ <th>rows</th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <% query.subqueries.each do |subquery| %>
17
+ <tr>
18
+ <td title="<%= h subquery.table %>"><%= h subquery.table %></td>
19
+ <td title="<%= h subquery.select_type %>"><%= h subquery.select_type %></td>
20
+ <td title="<%= h subquery.query_type %>"><%= h subquery.query_type %></td>
21
+ <td title="<%= h subquery.extra %>"><%= h subquery.extra %></td>
22
+ <td title="<%= h subquery.possible_keys %>"><%= h subquery.possible_keys %></td>
23
+ <td title="<%= h subquery.key %>"><%= h subquery.key %></td>
24
+ <td title="<%= h subquery.key_len %>"><%= h subquery.key_len %></td>
25
+ <td title="<%= h subquery.ref %>"><%= h subquery.ref %></td>
26
+ <td title="<%= h subquery.rows %>"><%= h subquery.rows %></td>
27
+ </tr>
28
+ <% end %>
29
+ </tbody>
30
+ </table>
@@ -0,0 +1,26 @@
1
+ <table>
2
+ <thead>
3
+ <tr>
4
+ <th>stage</th>
5
+ <th>duration</th>
6
+ <th>user cpu</th>
7
+ <th>context switches</th>
8
+ <th>block ops</th>
9
+ <th>messages</th>
10
+ <th>page faults</th>
11
+ </tr>
12
+ </thead>
13
+ <tbody>
14
+ <% query.profile.each do |profile| %>
15
+ <tr>
16
+ <td title="<%= h profile.Status %>"><%= h profile.Status %></td>
17
+ <td title="<%= h profile.Duration %> seconds"><%= h("%.4f" % profile.Duration.to_f) %>s</td>
18
+ <td title="USER: <%= profile.CPU_user.to_f %>s SYSTEM: <%= profile.CPU_system.to_f %>s"><%= h("%.4f" % profile.CPU_user.to_f) %>s</td>
19
+ <td title="Voluntary: <%= h profile.Context_voluntary %> Involuntary: <%= h profile.Context_involuntary %>"><%= h(profile.Context_voluntary.to_i + profile.Context_involuntary.to_i) %></td>
20
+ <td title="Ops in: <%= h profile.Block_ops_in %> Ops out: <%= h profile.Block_ops_out %>"><%= h(profile.Block_ops_in.to_i + profile.Block_ops_out.to_i) %></td>
21
+ <td title="Sent: <%= h profile.Messages_sent %> Received: <%= h profile.Messages_received %>"><%= h(profile.Messages_sent.to_i + profile.Messages_received.to_i) %></td>
22
+ <td title="Major: <%= h profile.Page_faults_major %> Minor: <%= h profile.Page_faults_minor %>"><%= h profile.Page_faults_major %></td>
23
+ <tr>
24
+ <% end %>
25
+ </tbody>
26
+ </table>
@@ -0,0 +1,8 @@
1
+ <span id="query_plain_sql_<%= query_sql.id %>" style="display: none;">
2
+ <%= syntax_highlighted_sql(query_sql.sql) %>
3
+ <% if query_sql.respond_to?(:subqueries) && query_sql.subqueries.length > 1 %>
4
+ <% end %>
5
+ </span>
6
+ <span id="query_sanitized_sql_<%= query_sql.id %>">
7
+ <%= syntax_highlighted_sql(query_sql.sanitized_sql) if query_sql.sanitized_sql %>
8
+ </span>
@@ -0,0 +1,31 @@
1
+ <div class="trace" id="trace_<%= query_id %>_abridged">
2
+ <code>
3
+ <% query_trace[0..(QueryReviewer::CONFIGURATION["stack_trace_lines"] - 1)].compact.each do |c| %>
4
+ <% if c.match(/:.*:/) %>
5
+ <% file = h(c.slice(0..(c.index(":", c.index(":")+1)-1))) %>
6
+ <span><%= file.split("/")[0..-2].join("/") %>/</span><span class="bold"><%= file.split("/").last %></span>
7
+ <br/>
8
+ <span class="indent"><%= h(c.slice((c.index(":", c.index(":")+1)+1)..-1)) %></span><br/>
9
+ <% else %>
10
+ <span><%= h c %></span><br/>
11
+ <% end %>
12
+ <% end %>
13
+ </code>
14
+ <a href="javascript: query_review_toggle('trace_<%= query_id %>_abridged'); query_review_toggle('trace_<%= query_id %>_full')" title="show full trace">FULL</a>
15
+ </div>
16
+
17
+ <div class="trace" style="display: none; max-height: 300px; overflow: scroll" id="trace_<%= query_id %>_full">
18
+ <code>
19
+ <% full_trace.compact.each do |c| %>
20
+ <% if c.match(/:.*:/) %>
21
+ <% file = h(c.slice(0..(c.index(":", c.index(":")+1)-1))) %>
22
+ <span><%= file.split("/")[0..-2].join("/") %>/</span><span class="bold"><%= file.split("/").last %></span>
23
+ <br/>
24
+ <span class="indent"><%= h(c.slice((c.index(":", c.index(":")+1)+1)..-1)) %></span><br/>
25
+ <% else %>
26
+ <span><%= h c %></span><br/>
27
+ <% end %>
28
+ <% end %>
29
+ </code>
30
+ <a href="javascript: query_review_toggle('trace_<%= query_id %>_abridged'); query_review_toggle('trace_<%= query_id %>_full')" title="show short trace">SHORT</a>
31
+ </div>
@@ -0,0 +1,54 @@
1
+ <li id="query_<%= query_with_warning.id %>">
2
+ <div>
3
+ <%= render :partial => "/spectrum", :locals => {:severity => query_with_warning.max_severity} %>
4
+ <% if QueryReviewer::CONFIGURATION["production_data"] %>
5
+ <div style="float: left; padding-right: 5px;">
6
+ <%= duration_with_color(query_with_warning) %>s
7
+ </div>
8
+ <% end %>
9
+ <p>
10
+ <% if query_with_warning.count > 1 %>
11
+ <b title="<%= query_with_warning.count %> queries were executed with the same stack trace and similar SQL structure">
12
+ <%= query_with_warning.count %> identical queries
13
+ </b>
14
+ <% end %>
15
+ <i>Table <%= (query_with_warning.warnings.detect {|w| !w.table.blank? } || query_with_warning.warnings.last).table %>:</i>
16
+ <% query_with_warning.warnings.sort{|a,b| a.severity <=> b.severity}.reverse.each_with_index do |warn, index| %>
17
+ <span style="color: <%= severity_color warn.severity%>;" title="<%= warn.desc%>"><%= warn.problem %></span><%= ", " if index < query_with_warning.warnings.length - 1 %>
18
+ <% end %>
19
+ <a href="javascript: query_review_toggle('warning_<%= query_with_warning.id %>_desc')" title="show/hide warning message">MSG</a>
20
+ <a href="javascript: query_review_toggle('warning_<%= query_with_warning.id %>_sql')" title="show/hide sql">SQL</a>
21
+ <% if query_with_warning.select? %>
22
+ <a href="javascript: query_review_toggle('warning_<%= query_with_warning.id %>_explain')" title="show/hide explain output">EXPLN</a>
23
+ <% end %>
24
+ <% if QueryReviewer::CONFIGURATION["profiling"] && query_with_warning.profile %>
25
+ <a href="javascript: query_review_toggle('warning_<%= query_with_warning.id %>_profile')" title="show/hide profile output">PROF</a>
26
+ <% end %>
27
+ <a href="javascript: query_review_toggle('warning_<%= query_with_warning.id %>_trace')" title="show/hide stack trace">TRACE</a>
28
+ <% if ignore_hash?(query_with_warning.to_hash) %>
29
+ <a href="javascript: remove_ignore_hash('<%= query_with_warning.to_hash %>'); query_review_hide('query_<%= query_with_warning.id %>')" title="stop ignore this query from now on">UNIGNR</a>
30
+ <% else %>
31
+ <a href="javascript: add_ignore_hash('<%= query_with_warning.to_hash %>'); query_review_hide('query_<%= query_with_warning.id %>')" title="ignoring this query from now on">IGNR</a>
32
+ <% end %>
33
+ </p>
34
+ </div>
35
+ <p style="display: none" id="warning_<%= query_with_warning.id %>_desc" class="indent">
36
+ <% query_with_warning.warnings.each do |warn| %>
37
+ <span style="color: <%= severity_color warn.severity%>"><%= warn.desc %></span><br/>
38
+ <% end %>
39
+ </p>
40
+ <p style="display: none" id="warning_<%= query_with_warning.id %>_sql" class="indent small tbpadded">
41
+ <%= render :partial => "/query_sql", :object => query_with_warning %>
42
+ </p>
43
+ <div style="display: none" id="warning_<%= query_with_warning.id %>_explain" class="indent small tbpadded">
44
+ <%= render :partial => "/explain", :locals => {:query => query_with_warning} %>
45
+ </div>
46
+ <% if QueryReviewer::CONFIGURATION["profiling"] && query_with_warning.profile %>
47
+ <div style="display: none" id="warning_<%= query_with_warning.id %>_profile" class="indent small tbpadded">
48
+ <%= render :partial => "/profile", :locals => {:query => query_with_warning} %>
49
+ </div>
50
+ <% end %>
51
+ <div style="display: none" id="warning_<%= query_with_warning.id %>_trace" class="indent small">
52
+ <%= render :partial => "/query_trace", :object => query_with_warning.relevant_trace, :locals => {:query_id => query_with_warning.id, :full_trace => query_with_warning.full_trace} %>
53
+ </div>
54
+ </li>
@@ -0,0 +1,10 @@
1
+ <div class="spectrum_container" title="Severity: <%= severity%> out of 10">
2
+ <% 0.upto(15) do |i| %>
3
+ <div class="spectrum_elem" style="background-color: <%= "##{i.to_s(16)}F0"%>"></div>
4
+ <% end %>
5
+ <% 0.upto(15) do |i| %>
6
+ <div class="spectrum_elem" style="background-color: <%= "#F#{((15-i)).to_s(16)}0"%>"></div>
7
+ <% end %>
8
+ <div class="spectrum_elem" style="background-color: #FF0000"></div>
9
+ <div class="spectrum_pointer" style="left: <%= severity * 3 + 2 %>px;"></div>
10
+ </div>
@@ -0,0 +1,8 @@
1
+ <li id="warning_<%= warning_no_query.id %>">
2
+ <div>
3
+ <%= render :partial => "/spectrum", :locals => {:severity => warning_no_query.severity} %>
4
+ <p>
5
+ <span style="color: <%= severity_color warning_no_query.severity%>" title="<%= warning_no_query.desc %>"><%= warning_no_query.problem %></span>
6
+ </p>
7
+ </div>
8
+ </li>
@@ -0,0 +1,97 @@
1
+ module QueryReviewer
2
+ module Views
3
+ module QueryReviewBoxHelper
4
+ def parent_div_class
5
+ "sql_#{parent_div_status.downcase}"
6
+ end
7
+
8
+ def parent_div_status
9
+ if !enabled_by_cookie
10
+ "DISABLED"
11
+ elsif overall_max_severity < (QueryReviewer::CONFIGURATION["warn_severity"] || 4)
12
+ "OK"
13
+ elsif overall_max_severity < (QueryReviewer::CONFIGURATION["critical_severity"] || 7)
14
+ # uh oh
15
+ "WARNING"
16
+ else
17
+ # oh @#&!
18
+ "CRITICAL"
19
+ end
20
+ end
21
+
22
+ def syntax_highlighted_sql(sql)
23
+ if QueryReviewer::CONFIGURATION["uv"]
24
+ uv_out = Uv.parse(sql, "xhtml", "sql_rails", false, "blackboard")
25
+ uv_out.gsub("<pre class=\"blackboard\">", "<code class=\"sql\">").gsub("</pre>", "</code>")
26
+ else
27
+ sql
28
+ end
29
+ end
30
+
31
+ def overall_max_severity
32
+ max = 0
33
+ max = queries_with_warnings_sorted_nonignored[0].max_severity unless queries_with_warnings_sorted_nonignored.empty?
34
+ max = warnings_no_query_sorted.first.severity unless warnings_no_query_sorted.empty? || warnings_no_query_sorted.first.severity < max
35
+ max
36
+ end
37
+
38
+ def severity_color(severity)
39
+ red = (severity * 16.0 / 10).to_i
40
+ green = ((10-severity) * 16.0 / 10).to_i
41
+ red = 8 if red > 8
42
+ red = 0 if red < 0
43
+ green = 8 if green > 8
44
+ green = 0 if green < 0
45
+ "##{red.to_s(16)}#{green.to_s(16)}0"
46
+ end
47
+
48
+ def ignore_hash?(h)
49
+ (@controller.send(:cookies)["query_review_ignore_list"] || "").split(",").include?(h.to_s)
50
+ end
51
+
52
+ def queries_with_warnings
53
+ @queries.queries.select{|q| q.has_warnings?}
54
+ end
55
+
56
+ def queries_with_warnings_sorted
57
+ queries_with_warnings.sort{|a,b| (b.max_severity * 1000 + (b.duration || 0)) <=> (a.max_severity * 1000 + (a.duration || 0))}
58
+ end
59
+
60
+ def queries_with_warnings_sorted_nonignored
61
+ queries_with_warnings_sorted.select{|q| q.max_severity >= ::QueryReviewer::CONFIGURATION["warn_severity"] && !ignore_hash?(q.to_hash)}
62
+ end
63
+
64
+ def queries_with_warnings_sorted_ignored
65
+ queries_with_warnings_sorted.reject{|q| q.max_severity >= ::QueryReviewer::CONFIGURATION["warn_severity"] && !ignore_hash?(q.to_hash)}
66
+ end
67
+
68
+ def warnings_no_query_sorted
69
+ @queries.collection_warnings.sort{|a,b| a.severity <=> b.severity}.reverse
70
+ end
71
+
72
+ def warnings_no_query_sorted_ignored
73
+ warnings_no_query_sorted.select{|q| q.severity < ::QueryReviewer::CONFIGURATION["warn_severity"]}
74
+ end
75
+
76
+ def warnings_no_query_sorted_nonignored
77
+ warnings_no_query_sorted.select{|q| q.severity >= ::QueryReviewer::CONFIGURATION["warn_severity"]}
78
+ end
79
+
80
+ def enabled_by_cookie
81
+ @controller.send(:cookies)["query_review_enabled"]
82
+ end
83
+
84
+ def duration_with_color(query)
85
+ title = query.duration_stats
86
+ duration = query.duration
87
+ if duration > QueryReviewer::CONFIGURATION["critical_duration_threshold"]
88
+ "<span style=\"color: #{severity_color(9)}\" title=\"#{title}\">#{"%.3f" % duration}</span>"
89
+ elsif duration > QueryReviewer::CONFIGURATION["warn_duration_threshold"]
90
+ "<span style=\"color: #{severity_color(QueryReviewer::CONFIGURATION["critical_severity"])}\" title=\"#{title}\">#{"%.3f" % duration}</span>"
91
+ else
92
+ "<span title=\"#{title}\">#{"%.3f" % duration}</span>"
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,39 @@
1
+ all:
2
+ inject_view: true
3
+ stack_trace_lines: 3
4
+ trace_includes_vendor: false
5
+ trace_includes_lib: true
6
+ profiling: enabled
7
+ production_data: true
8
+ max_safe_key_length: 100
9
+ disable_sql_cache: true
10
+
11
+ warn_severity: 3
12
+ critical_severity: 7
13
+
14
+ warn_select_count: 20
15
+ critical_select_count: 50
16
+
17
+ warn_update_count: 5
18
+ critical_update_count: 10
19
+
20
+ warn_insert_count: 5
21
+ critical_insert_count: 10
22
+
23
+ warn_delete_count: 5
24
+ critical_delete_count: 10
25
+
26
+ warn_duration_threshold: 0.2
27
+ critical_duration_threshold: 1.0
28
+
29
+ warn_affected_rows: 10
30
+ critical_affected_rows: 100
31
+
32
+ development:
33
+ enabled: true
34
+
35
+ production:
36
+ enabled: false
37
+
38
+ test:
39
+ enabled: false
data/rails/init.rb ADDED
@@ -0,0 +1,13 @@
1
+ # Include hook code here
2
+
3
+ require 'query_reviewer'
4
+
5
+ if QueryReviewer.enabled?
6
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:include, QueryReviewer::MysqlAdapterExtensions)
7
+ ActionController::Base.send(:include, QueryReviewer::ControllerExtensions)
8
+ Array.send(:include, QueryReviewer::ArrayExtensions)
9
+
10
+ if ActionController::Base.respond_to?(:append_view_path)
11
+ ActionController::Base.append_view_path(File.dirname(__FILE__) + "/lib/query_reviewer/views")
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ # desc "Explaining what the task does"
2
+ namespace :query_reviewer do
3
+ desc "Create a default config/query_reviewer.yml"
4
+ task :setup do
5
+ FileUtils.copy(File.join(File.dirname(__FILE__), "..", "query_reviewer_defaults.yml"), File.join(RAILS_ROOT, "config", "query_reviewer.yml"))
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class SqlQueryTest < Test::Unit::TestCase
4
+ # Replace this with your real tests.
5
+ def test_this_plugin
6
+ assert true
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class SqlSubqueryTest < Test::Unit::TestCase
4
+ def test_analyze_select_type
5
+ query = get_query(:select_type => "DEPENDENT UNION")
6
+ query.send :analyze_select_type!
7
+ query.should_warn("DEPENDENT UNION", 2)
8
+
9
+ query = get_query(:select_type => "UNCACHEABLE SUBQUERY")
10
+ query.send :analyze_select_type!
11
+ query.should_warn("UNCACHEABLE SUBQUERY", 10)
12
+ end
13
+
14
+ def get_query(options)
15
+ SqlSubQuery.new(options)
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ require "rubygems"
2
+ require "activesupport"
3
+ require 'test/unit'
4
+ require "query_reviewer"
5
+
6
+ module QueryReviewer
7
+ class SqlSubQuery
8
+ include Test::Unit::Assertions
9
+ def should_warn(problem, severity = nil)
10
+ assert self.warnings.detect{|warn| warn.problem.downcase == problem.downcase &&
11
+ (!severity || warn.severity == severity)}
12
+ end
13
+ end
14
+ end
15
+
16
+ class Test::Unit::TestCase
17
+ include QueryReviewer
18
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rdavila-query_reviewer
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Kongregate
13
+ - David Stevenson
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-04-26 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: |
23
+ This rails plugin not only runs "EXPLAIN" before each of your select queries in development, but provides a small DIV in the rendered output of each page with the summary of query warnings that it analyzed.
24
+
25
+ email: rdavila84@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files: []
31
+
32
+ files:
33
+ - MIT-LICENSE
34
+ - query_reviewer_defaults.yml
35
+ - Rakefile
36
+ - README
37
+ - init.rb
38
+ - lib/query_reviewer/sql_sub_query.rb
39
+ - lib/query_reviewer/array_extensions.rb
40
+ - lib/query_reviewer/views/_box_ajax.js
41
+ - lib/query_reviewer/views/_query_sql.rhtml
42
+ - lib/query_reviewer/views/_query_with_warning.rhtml
43
+ - lib/query_reviewer/views/_box_disabled.rhtml
44
+ - lib/query_reviewer/views/_explain.rhtml
45
+ - lib/query_reviewer/views/_query_trace.rhtml
46
+ - lib/query_reviewer/views/_box_includes.rhtml
47
+ - lib/query_reviewer/views/_box.rhtml
48
+ - lib/query_reviewer/views/_box_body.rhtml
49
+ - lib/query_reviewer/views/_box_header.rhtml
50
+ - lib/query_reviewer/views/_profile.rhtml
51
+ - lib/query_reviewer/views/_spectrum.rhtml
52
+ - lib/query_reviewer/views/query_review_box_helper.rb
53
+ - lib/query_reviewer/views/_warning_no_query.rhtml
54
+ - lib/query_reviewer/mysql_adapter_extensions.rb
55
+ - lib/query_reviewer/query_warning.rb
56
+ - lib/query_reviewer/controller_extensions.rb
57
+ - lib/query_reviewer/mysql_analyzer.rb
58
+ - lib/query_reviewer/sql_query.rb
59
+ - lib/query_reviewer/sql_query_collection.rb
60
+ - lib/query_reviewer.rb
61
+ - rails/init.rb
62
+ - tasks/query_reviewer_tasks.rake
63
+ - test/test_helper.rb
64
+ - test/sql_sub_query_test.rb
65
+ - test/sql_query_test.rb
66
+ has_rdoc: true
67
+ homepage: http://github.com/dsboulder/query_reviewer
68
+ licenses: []
69
+
70
+ post_install_message:
71
+ rdoc_options: []
72
+
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ requirements: []
90
+
91
+ rubyforge_project:
92
+ rubygems_version: 1.3.6
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: An advanced mysql query analyzer for rails
96
+ test_files: []
97
+