sql-logging 3.0.9 → 3.0.10
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.
- checksums.yaml +4 -4
- data/README.rdoc +23 -7
- data/lib/sql-logging.rb +17 -0
- data/lib/sql-logging/configuration.rb +51 -0
- data/lib/sql-logging/helper.rb +7 -0
- data/lib/sql-logging/railtie.rb +0 -4
- data/lib/sql-logging/statistics.rb +97 -86
- data/lib/sql-logging/version.rb +1 -1
- metadata +43 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9aac99fce0362d4f0b07f0035ec9266dc97e1599
|
|
4
|
+
data.tar.gz: 0c63f55de965eabc0216a84cad8f889eebbb9d58
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f135c72dd4b332fcf9e2f08b0ac4fb167090003d1a22ff161c0925f4d9910f973f51a0774e50426d022a3784eb32177ce749f181a7b84468e6e6afe433310335
|
|
7
|
+
data.tar.gz: 688b7ddf99783b4411d74ea76360775509871654a04e9dbf402fa8afad65a552112e4358f92562989a6f76699207c2be47b6e9b36682618d7b848040d99639b0
|
data/README.rdoc
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
= SQL Logging
|
|
2
2
|
|
|
3
|
+
{<img src="https://travis-ci.org/lightyear/sql-logging.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/lightyear/sql-logging]
|
|
4
|
+
|
|
3
5
|
The SQL Logging gem adds useful SQL debugging tools to ActiveRecord. The gem
|
|
4
6
|
is a repackaging of the old SQL Logging plug-in for Rails 2.x. It, in turn,
|
|
5
7
|
was based on ideas and plug-ins from Adam Doppelt's (standard logging
|
|
@@ -32,24 +34,38 @@ The Top 10 summary provides the most options to customize its behavior.
|
|
|
32
34
|
|
|
33
35
|
If you want more or fewer than 10 queries, you can easily change that:
|
|
34
36
|
|
|
35
|
-
SqlLogging
|
|
37
|
+
SqlLogging.configuration.top_sql_queries = 5
|
|
36
38
|
|
|
37
39
|
If you want the sorted by something other than the total execution time,
|
|
38
40
|
you can instead sort by the median execution time, number of executions
|
|
39
41
|
or rows/bytes returned over all executions of the query:
|
|
40
42
|
|
|
41
|
-
SqlLogging
|
|
42
|
-
SqlLogging
|
|
43
|
-
SqlLogging
|
|
44
|
-
SqlLogging
|
|
43
|
+
SqlLogging.configuration.show_top_sql_queries = :median_time
|
|
44
|
+
SqlLogging.configuration.show_top_sql_queries = :queries
|
|
45
|
+
SqlLogging.configuration.show_top_sql_queries = :rows
|
|
46
|
+
SqlLogging.configuration.show_top_sql_queries = :bytes
|
|
45
47
|
|
|
46
48
|
You can also turn the list off entirely:
|
|
47
49
|
|
|
48
|
-
SqlLogging
|
|
50
|
+
SqlLogging.configuration.show_top_sql_queries = false
|
|
49
51
|
|
|
50
52
|
If you don't want to see query backtraces:
|
|
51
53
|
|
|
52
|
-
SqlLogging
|
|
54
|
+
SqlLogging.configuration.show_sql_backtrace = false
|
|
55
|
+
|
|
56
|
+
=== General Configuration
|
|
57
|
+
|
|
58
|
+
In addition to the statistics configuration, more general aspects of the gem can
|
|
59
|
+
be set through the configuration.
|
|
60
|
+
|
|
61
|
+
For example, the logger can be set here to store results in alternate location:
|
|
62
|
+
|
|
63
|
+
SqlLogging.configuration.logger = ActiveSupport::Logger.new(STDOUT)
|
|
64
|
+
|
|
65
|
+
Additionally, the backtrace cleaner used for generating the call stack can be
|
|
66
|
+
configured here:
|
|
67
|
+
|
|
68
|
+
SqlLogging.configuration.backtrace_cleaner.remove_silencers!
|
|
53
69
|
|
|
54
70
|
= Acknowledgments
|
|
55
71
|
|
data/lib/sql-logging.rb
CHANGED
|
@@ -1 +1,18 @@
|
|
|
1
|
+
require 'rails'
|
|
2
|
+
require 'active_record'
|
|
3
|
+
require 'action_view'
|
|
4
|
+
require 'singleton'
|
|
5
|
+
|
|
6
|
+
require 'sql-logging/controller_runtime'
|
|
7
|
+
require 'sql-logging/configuration'
|
|
8
|
+
require 'sql-logging/helper'
|
|
9
|
+
require 'sql-logging/logged_query'
|
|
10
|
+
require 'sql-logging/statistics'
|
|
1
11
|
require 'sql-logging/railtie'
|
|
12
|
+
require 'sql-logging/version'
|
|
13
|
+
|
|
14
|
+
module SqlLogging
|
|
15
|
+
def self.configuration
|
|
16
|
+
@configuration ||= Configuration.new
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module SqlLogging
|
|
3
|
+
class Configuration
|
|
4
|
+
DEFAULTS = {
|
|
5
|
+
show_sql_backtrace: true,
|
|
6
|
+
show_top_sql_queries: :total_time,
|
|
7
|
+
top_sql_queries: 10,
|
|
8
|
+
backtrace_cleaner: nil,
|
|
9
|
+
logger: nil
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
attr_writer(*DEFAULTS.keys - [:show_top_sql_queries=])
|
|
13
|
+
attr_reader(*DEFAULTS.keys - %i(logger backtrace_cleaner))
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
DEFAULTS.each do |k, v|
|
|
17
|
+
send("#{k}=", v)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def show_top_sql_queries=(value)
|
|
22
|
+
validate_allowed([false, :rows, :queries, :bytes, :total_time,
|
|
23
|
+
:median_time], value)
|
|
24
|
+
@show_top_sql_queries = value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def backtrace_cleaner
|
|
28
|
+
@backtrace_cleaner ||= initialize_cleaner
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def logger
|
|
32
|
+
@logger ||= Rails.logger
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def initialize_cleaner
|
|
38
|
+
Rails.backtrace_cleaner.dup.tap do |cleaner|
|
|
39
|
+
cleaner.add_silencer { |line| line =~ %r{sql-logging/lib} }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def validate_allowed(allowed_values, value)
|
|
44
|
+
return if allowed_values.include?(value)
|
|
45
|
+
method_match = /`(.*)='/.match(caller(1, 1)[0])
|
|
46
|
+
method_name = method_match ? method_match[1] : 'Argument'
|
|
47
|
+
raise ArgumentError, "#{method_name} must be one of: " \
|
|
48
|
+
"#{allowed_values.map(&:inspect).join(', ')}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/sql-logging/railtie.rb
CHANGED
|
@@ -1,108 +1,119 @@
|
|
|
1
|
-
require 'singleton'
|
|
2
|
-
require 'sql-logging/logged_query'
|
|
3
|
-
|
|
4
1
|
module SqlLogging
|
|
5
|
-
class Helper
|
|
6
|
-
include Singleton
|
|
7
|
-
include ActionView::Helpers::TextHelper
|
|
8
|
-
include ActionView::Helpers::NumberHelper
|
|
9
|
-
end
|
|
10
|
-
|
|
11
2
|
class Statistics
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def self.show_top_sql_queries
|
|
20
|
-
@@show_top_sql_queries
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def self.show_top_sql_queries=(value)
|
|
24
|
-
unless [ false, :rows, :queries, :bytes, :total_time, :median_time ].include?(value)
|
|
25
|
-
raise ArgumentError, "show_top_sql_queries must be one of false, :rows, :queries, :bytes, :total_time or :median_time"
|
|
3
|
+
class Data
|
|
4
|
+
COUNTERS = %i(queries bytes rows).freeze
|
|
5
|
+
attr_accessor(*COUNTERS)
|
|
6
|
+
attr_reader(:top_queries)
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
reset!
|
|
26
10
|
end
|
|
27
|
-
|
|
28
|
-
@@show_top_sql_queries = value
|
|
29
|
-
end
|
|
30
11
|
|
|
31
|
-
|
|
32
|
-
|
|
12
|
+
def reset!
|
|
13
|
+
COUNTERS.each { |v| instance_variable_set("@#{v}", 0) }
|
|
14
|
+
@top_queries = {}
|
|
15
|
+
end
|
|
33
16
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
17
|
+
def add_query(row_counts, bytes)
|
|
18
|
+
@queries += 1
|
|
19
|
+
@bytes += bytes
|
|
20
|
+
@rows += row_counts
|
|
21
|
+
end
|
|
37
22
|
end
|
|
38
23
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
24
|
+
class << self
|
|
25
|
+
extend Forwardable
|
|
26
|
+
|
|
27
|
+
def configuration
|
|
28
|
+
SqlLogging.configuration
|
|
43
29
|
end
|
|
44
|
-
@@backtrace_cleaner
|
|
45
|
-
end
|
|
46
30
|
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
def_delegators :configuration, :show_sql_backtrace, :top_sql_queries,
|
|
32
|
+
:show_top_sql_queries, :backtrace_cleaner,
|
|
33
|
+
:show_top_sql_queries=, :show_sql_backtrace=,
|
|
34
|
+
:top_sql_queries=, :logger
|
|
35
|
+
|
|
36
|
+
def data
|
|
37
|
+
@data ||= Data.new
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def_delegator :data, :top_queries
|
|
41
|
+
|
|
42
|
+
def reset_statistics!
|
|
43
|
+
data.reset!
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def record_query(sql, name, msec, result)
|
|
47
|
+
return if name.blank? || name =~ / Columns$/ || name == :skip_logging
|
|
48
|
+
ntuples, bytes = tuples_and_bytes_in_result(result)
|
|
49
|
+
|
|
50
|
+
data.add_query(ntuples, bytes)
|
|
51
|
+
|
|
52
|
+
backtrace = backtrace_cleaner.clean(caller).join("\n ")
|
|
53
|
+
add_query_to_top_queries(sql, name, backtrace, msec, ntuples, bytes)
|
|
54
|
+
|
|
55
|
+
logger.debug " #{ntuples} rows, #{bytes} bytes"
|
|
56
|
+
logger.debug " #{backtrace}" if show_sql_backtrace
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def log_report
|
|
60
|
+
logger.debug "SQL Logging: #{data.queries} statements executed" \
|
|
61
|
+
", returning #{data.bytes} bytes"
|
|
62
|
+
|
|
63
|
+
return unless top_queries?
|
|
64
|
+
logger.debug "Top #{top_sql_queries} SQL executions:"
|
|
65
|
+
sorted_keys = top_queries.keys.sort_by do |k|
|
|
66
|
+
top_queries[k][show_top_sql_queries]
|
|
67
|
+
end.reverse
|
|
68
|
+
sorted_keys.slice(0..top_sql_queries).each do |key|
|
|
69
|
+
query = top_queries[key]
|
|
70
|
+
logger.debug format(
|
|
71
|
+
' Executed %d times in %.1fms (%.1f/%.1f/%.1fms min/median/max),' \
|
|
72
|
+
" returning %d rows(%d bytes):\n"\
|
|
73
|
+
" %s\n" \
|
|
74
|
+
" First exec was: %s\n" \
|
|
75
|
+
' %s', query.queries, query.total_time, query.min_time,
|
|
76
|
+
query.median_time, query.max_time, query.rows, query.bytes,
|
|
77
|
+
query.name, query.sql, query.backtrace
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def add_query_to_top_queries(sql, name, backtrace, msec, ntuples, bytes)
|
|
85
|
+
return unless show_top_sql_queries
|
|
86
|
+
key = "#{name}:#{backtrace}"
|
|
87
|
+
top_queries[key] ||= LoggedQuery.new(sql, name, backtrace)
|
|
88
|
+
top_queries[key].log_query(ntuples, bytes, msec)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def tuples_and_bytes_in_result(result)
|
|
49
92
|
bytes = 0
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
93
|
+
ntuples = 0
|
|
94
|
+
return [ntuples, bytes] if result.nil?
|
|
95
|
+
if result.respond_to?(:each)
|
|
53
96
|
result.each do |row|
|
|
54
97
|
row.each do |key, value|
|
|
55
98
|
bytes += key.size if key && key.respond_to?(:size)
|
|
56
99
|
bytes += value.size if value && value.respond_to?(:size)
|
|
57
100
|
end
|
|
58
|
-
end if result.respond_to?(:each)
|
|
59
|
-
ntuples = 0
|
|
60
|
-
if result.respond_to?(:length)
|
|
61
|
-
ntuples = result.length
|
|
62
|
-
elsif result.respond_to?(:count)
|
|
63
|
-
ntuples = result.count
|
|
64
|
-
elsif result.respond_to?(:num_rows)
|
|
65
|
-
ntuples = result.num_rows
|
|
66
|
-
elsif result.respond_to?(:ntuples)
|
|
67
|
-
ntuples = result.ntuples
|
|
68
101
|
end
|
|
69
102
|
end
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
unless query = @@top_queries[key]
|
|
79
|
-
query = LoggedQuery.new(sql, name, backtrace)
|
|
80
|
-
@@top_queries[key] = query
|
|
81
|
-
end
|
|
82
|
-
query.log_query(ntuples || 0, bytes || 0, msec)
|
|
103
|
+
if result.respond_to?(:length)
|
|
104
|
+
ntuples = result.length
|
|
105
|
+
elsif result.respond_to?(:count)
|
|
106
|
+
ntuples = result.count
|
|
107
|
+
elsif result.respond_to?(:num_rows)
|
|
108
|
+
ntuples = result.num_rows
|
|
109
|
+
elsif result.respond_to?(:ntuples)
|
|
110
|
+
ntuples = result.ntuples
|
|
83
111
|
end
|
|
84
|
-
|
|
85
|
-
Rails.logger.debug " #{ntuples} rows, #{bytes} bytes"
|
|
86
|
-
Rails.logger.debug " #{backtrace}" if @@show_sql_backtrace
|
|
112
|
+
[ntuples, bytes]
|
|
87
113
|
end
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
Rails.logger.debug "SQL Logging: #{@@queries} statements executed, returning #{@@bytes} bytes"
|
|
92
|
-
|
|
93
|
-
unless @@show_top_sql_queries == false || @@top_queries.empty?
|
|
94
|
-
Rails.logger.debug "Top #{@@top_sql_queries} SQL executions:"
|
|
95
|
-
sorted_keys = @@top_queries.keys.sort_by { |k| @@top_queries[k][@@show_top_sql_queries] }.reverse
|
|
96
|
-
sorted_keys.slice(0..@@top_sql_queries).each do |key|
|
|
97
|
-
query = @@top_queries[key]
|
|
98
|
-
Rails.logger.debug " Executed #{query.queries} times in #{'%.1f' % query.total_time}ms " +
|
|
99
|
-
"(#{'%.1f' % query.min_time}/#{'%.1f' % query.median_time}/#{'%.1f' % query.max_time}ms min/median/max), " +
|
|
100
|
-
"returning #{query.rows} rows" +
|
|
101
|
-
"(#{query.bytes} bytes):\n" +
|
|
102
|
-
" #{query.name}\n" +
|
|
103
|
-
" First exec was: #{query.sql}\n" +
|
|
104
|
-
" #{query.backtrace}"
|
|
105
|
-
end
|
|
114
|
+
|
|
115
|
+
def top_queries?
|
|
116
|
+
show_top_sql_queries && !top_queries.empty?
|
|
106
117
|
end
|
|
107
118
|
end
|
|
108
119
|
end
|
data/lib/sql-logging/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,48 +1,64 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sql-logging
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steve Madsen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2017-01-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - '>='
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
20
|
-
- - "<"
|
|
21
|
-
- !ruby/object:Gem::Version
|
|
22
|
-
version: '5'
|
|
19
|
+
version: '4.0'
|
|
23
20
|
type: :runtime
|
|
24
21
|
prerelease: false
|
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
23
|
requirements:
|
|
27
|
-
- -
|
|
24
|
+
- - '>='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '4.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: minitest
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - '>='
|
|
28
46
|
- !ruby/object:Gem::Version
|
|
29
|
-
version:
|
|
30
|
-
|
|
47
|
+
version: 2.11.0
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - '>='
|
|
31
53
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
33
|
-
description:
|
|
34
|
-
sql-logging adds functionality to Rails to debug query performance.
|
|
35
|
-
SQL triggered by your application includes a stack trace so you can determine
|
|
36
|
-
what code led to it, and a summary is emitted after each request to highlight
|
|
37
|
-
the most expensive queries executed.
|
|
54
|
+
version: 2.11.0
|
|
55
|
+
description: Adds SQL analysis and debugging info to Rails 3 apps.
|
|
38
56
|
email: steve@lightyearsoftware.com
|
|
39
57
|
executables: []
|
|
40
58
|
extensions: []
|
|
41
59
|
extra_rdoc_files:
|
|
42
60
|
- README.rdoc
|
|
43
61
|
files:
|
|
44
|
-
- README.rdoc
|
|
45
|
-
- lib/sql-logging.rb
|
|
46
62
|
- lib/sql-logging/adapters/cache_extension.rb
|
|
47
63
|
- lib/sql-logging/adapters/mysql.rb
|
|
48
64
|
- lib/sql-logging/adapters/mysql2.rb
|
|
@@ -50,14 +66,17 @@ files:
|
|
|
50
66
|
- lib/sql-logging/adapters/postgresql.rb
|
|
51
67
|
- lib/sql-logging/adapters/sqlite.rb
|
|
52
68
|
- lib/sql-logging/adapters/sqlite3.rb
|
|
69
|
+
- lib/sql-logging/configuration.rb
|
|
53
70
|
- lib/sql-logging/controller_runtime.rb
|
|
71
|
+
- lib/sql-logging/helper.rb
|
|
54
72
|
- lib/sql-logging/logged_query.rb
|
|
55
73
|
- lib/sql-logging/railtie.rb
|
|
56
74
|
- lib/sql-logging/statistics.rb
|
|
57
75
|
- lib/sql-logging/version.rb
|
|
76
|
+
- lib/sql-logging.rb
|
|
77
|
+
- README.rdoc
|
|
58
78
|
homepage: http://github.com/lightyear/sql-logging
|
|
59
|
-
licenses:
|
|
60
|
-
- MIT
|
|
79
|
+
licenses: []
|
|
61
80
|
metadata: {}
|
|
62
81
|
post_install_message:
|
|
63
82
|
rdoc_options: []
|
|
@@ -65,18 +84,18 @@ require_paths:
|
|
|
65
84
|
- lib
|
|
66
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
86
|
requirements:
|
|
68
|
-
- -
|
|
87
|
+
- - '>='
|
|
69
88
|
- !ruby/object:Gem::Version
|
|
70
89
|
version: '0'
|
|
71
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
91
|
requirements:
|
|
73
|
-
- -
|
|
92
|
+
- - '>='
|
|
74
93
|
- !ruby/object:Gem::Version
|
|
75
94
|
version: '0'
|
|
76
95
|
requirements: []
|
|
77
96
|
rubyforge_project:
|
|
78
|
-
rubygems_version: 2.
|
|
97
|
+
rubygems_version: 2.0.14.1
|
|
79
98
|
signing_key:
|
|
80
99
|
specification_version: 4
|
|
81
|
-
summary: Adds SQL analysis and debugging info to Rails 3
|
|
100
|
+
summary: Adds SQL analysis and debugging info to Rails 3 apps.
|
|
82
101
|
test_files: []
|