pghero 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pghero might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f906933d95e8d3370c8c5e0770bea23b496d3163
4
- data.tar.gz: 26d9a09ea1d0a655ef91247c5d10eb01f63e4c92
3
+ metadata.gz: 924297f7d5d7f5ae69f7105d22752011a809c6e3
4
+ data.tar.gz: 8fcd272469562fd914aa9ad490a6cb1ba022ad56
5
5
  SHA512:
6
- metadata.gz: 1967500ff522d67529f888cb03b47533fdf2ac4dcef9f316bf9ef3b135e6fdbc0c7edf9d47260f85547e818dd517e0daea5507601a07060ac3793eb4f28dfe2b
7
- data.tar.gz: 9593a2e9a276fa692d6ea085313406cf5ca04c1b8ec50365137b807572bfdccdb6809d2098477b331a0ce28eec0ea44d8bc97887555747d94889faea9d3dacaa
6
+ metadata.gz: b83d39c5c5939966dfffbe4c0f6013a1f0830e8cf12821d9c46460a0ab8e55b82a0ba1273159a0c3c309d0259ae50060cb4538de4dddd0cf69918d1f8586f881
7
+ data.tar.gz: 2932ffba7ddd3545085858e3ec26a08a1d5c119a80fd8ee297ef947335128e0210ee5450b6d0345c23861d97978c6ec2408536629e19e4d4e82398f74b8e28fa
data/.gitignore CHANGED
@@ -3,7 +3,7 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
- Gemfile.lock
6
+ *.lock
7
7
  InstalledFiles
8
8
  _yardoc
9
9
  coverage
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.1.1
2
+
3
+ - Added explain
4
+ - Added query stats
5
+ - Fixed CSS issues
6
+
1
7
  ## 0.1.0
2
8
 
3
9
  - First major release
data/README.md CHANGED
@@ -4,13 +4,13 @@ Database insights made easy
4
4
 
5
5
  [View the demo](https://pghero.herokuapp.com/)
6
6
 
7
- ![Screenshot](https://pghero.herokuapp.com/assets/screenshot-f7b70ae13b1f2ab0ea44ad005208c477.png)
7
+ ![Screenshot](https://pghero.herokuapp.com/assets/screenshot-57d07895ddb050f8dd46b98e73ef1415.png)
8
8
 
9
9
  Supports PostgreSQL 9.2+
10
10
 
11
11
  For pure SQL, check out [PgHero.sql](https://github.com/ankane/pghero.sql)
12
12
 
13
- Initial queries by [Heroku](https://blog.heroku.com/archives/2013/5/10/more_insight_into_your_database_with_pgextras) :clap:
13
+ A big thanks to [Craig Kerstiens](http://www.craigkerstiens.com/2013/01/10/more-on-postgres-performance/) and [Heroku](https://blog.heroku.com/archives/2013/5/10/more_insight_into_your_database_with_pgextras) for the initial queries :clap:
14
14
 
15
15
  ## Installation
16
16
 
@@ -68,13 +68,15 @@ end
68
68
 
69
69
  ## TODO
70
70
 
71
- - better explanations on dashboard
71
+ - show exactly which indexes to add
72
72
  - use `pg_stat_statements` extension for more detailed insights
73
- - poll for running queries
73
+ - more detailed explanations on dashboard
74
+
75
+ Know a bit about PostgreSQL? [Suggestions](https://github.com/ankane/pghero/issues) are greatly appreciated.
74
76
 
75
77
  ## Thanks
76
78
 
77
- Thanks to [Heroku](https://blog.heroku.com/archives/2013/5/10/more_insight_into_your_database_with_pgextras) for the initial queries and [Bootswatch](https://github.com/thomaspark/bootswatch) for the theme.
79
+ Thanks to [Craig Kerstiens](http://www.craigkerstiens.com/2013/01/10/more-on-postgres-performance/) and [Heroku](https://blog.heroku.com/archives/2013/5/10/more_insight_into_your_database_with_pgextras) for the initial queries and [Bootswatch](https://github.com/thomaspark/bootswatch) for the theme.
78
80
 
79
81
  ## History
80
82
 
data/Rakefile CHANGED
@@ -1,2 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rake/testtask"
2
3
 
4
+ task :default => :test
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.pattern = "test/**/*_test.rb"
8
+ end
@@ -6,14 +6,19 @@ module PgHero
6
6
 
7
7
  http_basic_authenticate_with name: ENV["PGHERO_USERNAME"], password: ENV["PGHERO_PASSWORD"] if ENV["PGHERO_PASSWORD"]
8
8
 
9
+ before_filter :set_query_stats_enabled
10
+
9
11
  def index
10
12
  @title = "Status"
13
+ @slow_queries = PgHero.slow_queries
11
14
  @long_running_queries = PgHero.long_running_queries
12
15
  @index_hit_rate = PgHero.index_hit_rate
13
16
  @table_hit_rate = PgHero.table_hit_rate
14
17
  @missing_indexes = PgHero.missing_indexes
15
18
  @unused_indexes = PgHero.unused_indexes
16
19
  @good_cache_rate = @table_hit_rate >= 0.99 && @index_hit_rate >= 0.99
20
+ @query_stats_available = PgHero.query_stats_available?
21
+ @rds = PgHero.rds?
17
22
  end
18
23
 
19
24
  def indexes
@@ -32,6 +37,25 @@ module PgHero
32
37
  @running_queries = PgHero.running_queries
33
38
  end
34
39
 
40
+ def query_stats
41
+ @title = "Query Stats"
42
+ @query_stats = PgHero.query_stats
43
+ end
44
+
45
+ def explain
46
+ @title = "Explain"
47
+ @query = params[:query]
48
+ # TODO use get + token instead of post so users can share links
49
+ # need to prevent CSRF and DoS
50
+ if request.post? and @query
51
+ begin
52
+ @explanation = PgHero.explain(@query)
53
+ rescue ActiveRecord::StatementInvalid => e
54
+ @error = e.message
55
+ end
56
+ end
57
+ end
58
+
35
59
  def kill
36
60
  if PgHero.kill(params[:pid])
37
61
  redirect_to root_path, notice: "Query killed"
@@ -45,5 +69,29 @@ module PgHero
45
69
  redirect_to :back, notice: "Connections killed"
46
70
  end
47
71
 
72
+ def enable_query_stats
73
+ begin
74
+ PgHero.enable_query_stats
75
+ redirect_to :back, notice: "Query stats enabled"
76
+ rescue ActiveRecord::StatementInvalid => e
77
+ redirect_to :back, alert: "The database user does not have permission to enable query stats"
78
+ end
79
+ end
80
+
81
+ def reset_query_stats
82
+ begin
83
+ PgHero.reset_query_stats
84
+ redirect_to :back, notice: "Query stats reset"
85
+ rescue ActiveRecord::StatementInvalid => e
86
+ redirect_to :back, alert: "The database user does not have permission to reset query stats"
87
+ end
88
+ end
89
+
90
+ protected
91
+
92
+ def set_query_stats_enabled
93
+ @query_stats_enabled = PgHero.query_stats_enabled?
94
+ end
95
+
48
96
  end
49
97
  end
@@ -7,13 +7,16 @@
7
7
 
8
8
  <style>
9
9
  body {
10
- font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
11
10
  margin: 0;
12
11
  padding: 20px;
12
+ background-color: #2b3e50;
13
+ }
14
+
15
+ body, textarea {
16
+ font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
13
17
  font-size: 14px;
14
18
  line-height: 1.4;
15
19
  color: #333;
16
- background-color: #2b3e50;
17
20
  }
18
21
 
19
22
  a, a:visited, a:active {
@@ -67,6 +70,13 @@
67
70
  white-space: pre-wrap;
68
71
  }
69
72
 
73
+ textarea {
74
+ width: 100%;
75
+ height: 80px;
76
+ border: solid 1px #ccc;
77
+ padding: 10px;
78
+ }
79
+
70
80
  .container {
71
81
  max-width: 1000px;
72
82
  margin-left: auto;
@@ -124,6 +134,8 @@
124
134
  cursor: pointer;
125
135
  outline: none;
126
136
  margin: 0;
137
+ -webkit-appearance: none;
138
+ -webkit-border-radius: 0;
127
139
  }
128
140
 
129
141
  .btn-danger {
@@ -153,6 +165,10 @@
153
165
  background-color: #f0ad4e;
154
166
  }
155
167
 
168
+ .alert-danger {
169
+ background-color: #df691a;
170
+ }
171
+
156
172
  .alert a {
157
173
  color: #fff;
158
174
  text-decoration: none;
@@ -312,8 +328,10 @@
312
328
  </head>
313
329
  <body>
314
330
  <div class="container">
315
- <div class="alert alert-info">
316
- <% if notice %>
331
+ <div class="alert alert-<%= alert ? "danger" : "info" %>">
332
+ <% if alert %>
333
+ <%= alert %>
334
+ <% elsif notice %>
317
335
  <%= notice %>
318
336
  <% elsif Rails.env.development? %>
319
337
  Do not use development information to make decisions about your production environment
@@ -327,9 +345,13 @@
327
345
  <ul class="nav">
328
346
  <!-- poor man's active_link_to -->
329
347
  <li class="<%= controller.action_name == "index" ? "active" : "" %>"><%= link_to "Status", root_path %></li>
348
+ <% if @query_stats_enabled %>
349
+ <li class="<%= controller.action_name == "query_stats" ? "active" : "" %>"><%= link_to "Queries", query_stats_path %></li>
350
+ <% end %>
330
351
  <li class="<%= controller.action_name == "indexes" ? "active" : "" %>"><%= link_to "Indexes", indexes_path %></li>
331
352
  <li class="<%= controller.action_name == "space" ? "active" : "" %>"><%= link_to "Space", space_path %></li>
332
- <li class="<%= controller.action_name == "queries" ? "active" : "" %>"><%= link_to "Queries", queries_path %></li>
353
+ <li class="<%= controller.action_name == "explain" ? "active" : "" %>"><%= link_to "Explain", explain_path %></li>
354
+ <li class="<%= controller.action_name == "queries" ? "active" : "" %>"><%= link_to "Live Queries", queries_path %></li>
333
355
  </ul>
334
356
  </div>
335
357
 
@@ -0,0 +1,21 @@
1
+ <table class="table">
2
+ <thead>
3
+ <tr>
4
+ <th style="width: 33.33%;">Total Time</th>
5
+ <th style="width: 33.33%;">Average Time</th>
6
+ <th style="width: 33.33%;">Calls</th>
7
+ </tr>
8
+ </thead>
9
+ <tbody>
10
+ <% queries.each do |query| %>
11
+ <tr>
12
+ <td><%= query["total_minutes"].to_f.round %> min</td>
13
+ <td><%= query["average_time"].to_f.round %> ms</td>
14
+ <td><% calls = query["calls"].to_i %><%= calls >= 1000 ? number_with_delimiter(calls.round) + "k" : calls %></td>
15
+ </tr>
16
+ <tr>
17
+ <td colspan="3" style="border-top: none; padding: 0;"><pre><%= query["query"] %></pre></td>
18
+ </tr>
19
+ <% end %>
20
+ </tbody>
21
+ </table>
@@ -0,0 +1,15 @@
1
+ <div class="content">
2
+ <h1>Explain</h1>
3
+
4
+ <%= form_tag explain_path do %>
5
+ <%= text_area_tag :query, @query, placeholder: "Enter a SQL query" %>
6
+ <p><%= submit_tag "Explain", class: "btn btn-info" %></p>
7
+ <% end %>
8
+
9
+ <% if @explanation %>
10
+ <pre><%= @explanation %></pre>
11
+ <p><%= link_to "See how to interpret this", "http://www.postgresql.org/docs/current/static/using-explain.html", target: "_blank" %></p>
12
+ <% elsif @error %>
13
+ <div class="alert alert-danger"><%= @error %></div>
14
+ <% end %>
15
+ </div>
@@ -1,4 +1,20 @@
1
1
  <div id="status">
2
+ <div class="alert alert-<%= @query_stats_enabled ? "success" : "warning" %>">
3
+ <% if @query_stats_enabled %>
4
+ Query stats are enabled
5
+ <% else %>
6
+ Query stats are not enabled
7
+ <% end %>
8
+ </div>
9
+ <div class="alert alert-<%= @query_stats_enabled && @slow_queries.empty? ? "success" : "warning" %>">
10
+ <% if !@query_stats_enabled %>
11
+ Query stats must be enabled for slow queries
12
+ <% elsif @slow_queries.any? %>
13
+ <%= pluralize(@slow_queries.size, "slow query") %>
14
+ <% else %>
15
+ No slow queries
16
+ <% end %>
17
+ </div>
2
18
  <div class="alert alert-<%= @long_running_queries.empty? ? "success" : "warning" %>">
3
19
  <% if @long_running_queries.any? %>
4
20
  <%= pluralize(@long_running_queries.size, "long running query") %>
@@ -29,6 +45,37 @@
29
45
  </div>
30
46
  </div>
31
47
 
48
+ <% if !@query_stats_enabled %>
49
+ <div class="content">
50
+ <h1>Query Statistics</h1>
51
+
52
+ <% if @query_stats_available %>
53
+ <p>
54
+ Query statistics are available but not enabled.
55
+ <%= button_to "Enable", enable_query_stats_path, class: "btn btn-info" %>
56
+ </p>
57
+ <% elsif @rds %>
58
+ <p>Query statistics are not available on Amazon RDS. <%= link_to "Tell Amazon you want this.", "https://forums.aws.amazon.com/thread.jspa?messageID=548724", target: "_blank" %></p>
59
+ <% else %>
60
+ <p>Make them available by adding the following lines to <code>postgresql.conf</code>:</p>
61
+ <pre>shared_preload_libraries = 'pg_stat_statements'
62
+ pg_stat_statements.track = all</pre>
63
+ <p>Restart the server for the changes to take effect.</p>
64
+ <% end %>
65
+ </div>
66
+ <% end %>
67
+
68
+ <% if @query_stats_enabled && @slow_queries.any? %>
69
+ <div class="content">
70
+ <h1>Slow Queries</h1>
71
+
72
+ <p>Slow queries take 20 ms or more on average and have been called at least 100 times.</p>
73
+ <p><%= link_to "Run EXPLAIN", explain_path %> on queries to see where to add indexes.</p>
74
+
75
+ <%= render partial: "query_stats_table", locals: {queries: @slow_queries} %>
76
+ </div>
77
+ <% end %>
78
+
32
79
  <% if @long_running_queries.any? %>
33
80
  <div class="content">
34
81
  <h1>Long Running Queries</h1>
@@ -1,5 +1,5 @@
1
1
  <div class="content">
2
- <h1>Queries</h1>
2
+ <h1>Live Queries</h1>
3
3
 
4
4
  <%= render partial: "queries_table", locals: {queries: @running_queries} %>
5
5
 
@@ -0,0 +1,17 @@
1
+ <div class="content">
2
+ <% if @query_stats_enabled %>
3
+ <%= button_to "Reset", reset_query_stats_path, class: "btn btn-danger", style: "float: right;" %>
4
+ <% end %>
5
+
6
+ <h1>Queries</h1>
7
+
8
+ <% if @query_stats_enabled %>
9
+ <% if @query_stats.any? %>
10
+ <%= render partial: "query_stats_table", locals: {queries: @query_stats} %>
11
+ <% else %>
12
+ <p>Stats are not available yet. Come back soon!</p>
13
+ <% end %>
14
+ <% else %>
15
+ <p>Query stats are not enabled.</p>
16
+ <% end %>
17
+ </div>
data/config/routes.rb CHANGED
@@ -3,7 +3,12 @@ PgHero::Engine.routes.draw do
3
3
  get "indexes", to: "home#indexes"
4
4
  get "space", to: "home#space"
5
5
  get "queries", to: "home#queries"
6
+ get "query_stats", to: "home#query_stats"
7
+ get "explain", to: "home#explain"
6
8
 
7
9
  post "kill", to: "home#kill"
8
10
  post "kill_all", to: "home#kill_all"
11
+ post "enable_query_stats", to: "home#enable_query_stats"
12
+ post "explain", to: "home#explain"
13
+ post "reset_query_stats", to: "home#reset_query_stats"
9
14
  end
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in searchkick.gemspec
4
+ gemspec path: "../"
5
+
6
+ gem "activerecord", "~> 3.1.0"
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in searchkick.gemspec
4
+ gemspec path: "../"
5
+
6
+ gem "activerecord", "~> 3.2.0"
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in searchkick.gemspec
4
+ gemspec path: "../"
5
+
6
+ gem "activerecord", "~> 4.0.0"
data/lib/pghero.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "pghero/version"
2
+ require "active_record"
2
3
  require "pghero/engine" if defined?(Rails)
3
4
 
4
5
  module PgHero
@@ -169,7 +170,7 @@ module PgHero
169
170
  end
170
171
 
171
172
  def kill(pid)
172
- connection.execute("SELECT pg_cancel_backend(#{pid.to_i})").first["pg_cancel_backend"] == "t"
173
+ execute("SELECT pg_cancel_backend(#{pid.to_i})").first["pg_cancel_backend"] == "t"
173
174
  end
174
175
 
175
176
  def kill_all
@@ -185,14 +186,137 @@ module PgHero
185
186
  true
186
187
  end
187
188
 
189
+ # http://www.craigkerstiens.com/2013/01/10/more-on-postgres-performance/
190
+ def query_stats
191
+ if query_stats_enabled?
192
+ select_all %Q{
193
+ SELECT
194
+ query,
195
+ (total_time / 1000 / 60) as total_minutes,
196
+ (total_time / calls) as average_time,
197
+ calls
198
+ FROM
199
+ pg_stat_statements
200
+ INNER JOIN
201
+ pg_database ON pg_database.oid = pg_stat_statements.dbid
202
+ WHERE
203
+ pg_database.datname = current_database()
204
+ ORDER BY
205
+ total_minutes DESC
206
+ LIMIT 100
207
+ }
208
+ else
209
+ []
210
+ end
211
+ end
212
+
213
+ def slow_queries
214
+ if query_stats_enabled?
215
+ select_all %Q{
216
+ SELECT
217
+ query,
218
+ (total_time / 1000 / 60) as total_minutes,
219
+ (total_time / calls) as average_time,
220
+ calls
221
+ FROM
222
+ pg_stat_statements
223
+ INNER JOIN
224
+ pg_database ON pg_database.oid = pg_stat_statements.dbid
225
+ WHERE
226
+ pg_database.datname = current_database()
227
+ AND calls >= 100
228
+ AND (total_time / calls) >= 20
229
+ ORDER BY
230
+ total_minutes DESC
231
+ LIMIT 100
232
+ }
233
+ else
234
+ []
235
+ end
236
+ end
237
+
238
+ def query_stats_available?
239
+ select_all("SELECT COUNT(*) AS count FROM pg_available_extensions WHERE name = 'pg_stat_statements'").first["count"].to_i > 0
240
+ end
241
+
242
+ def query_stats_enabled?
243
+ select_all("SELECT COUNT(*) AS count FROM pg_extension WHERE extname = 'pg_stat_statements'").first["count"].to_i > 0 && query_stats_readable?
244
+ end
245
+
246
+ def query_stats_readable?
247
+ begin
248
+ # ensure the user has access to the table
249
+ select_all("SELECT has_table_privilege(current_user, 'pg_stat_statements', 'SELECT')").first["has_table_privilege"] == "t"
250
+ rescue ActiveRecord::StatementInvalid
251
+ false
252
+ end
253
+ end
254
+
255
+ def enable_query_stats
256
+ execute("CREATE EXTENSION pg_stat_statements")
257
+ end
258
+
259
+ def disable_query_stats
260
+ execute("DROP EXTENSION IF EXISTS pg_stat_statements")
261
+ true
262
+ end
263
+
264
+ def reset_query_stats
265
+ if query_stats_enabled?
266
+ execute("SELECT pg_stat_statements_reset()")
267
+ true
268
+ else
269
+ false
270
+ end
271
+ end
272
+
273
+ def rds?
274
+ !!(Connection.connection_config[:host].to_s =~ /rds\.amazonaws\.com\z/)
275
+ end
276
+
277
+ def explain(sql)
278
+ sql = squish(sql)
279
+ explanation = nil
280
+ explain_safe = explain_safe?
281
+
282
+ # use transaction for safety
283
+ Connection.transaction do
284
+ if !explain_safe and (sql.sub(/;\z/, "").include?(";") or sql.upcase.include?("COMMIT"))
285
+ raise ActiveRecord::StatementInvalid, "Unsafe statement"
286
+ end
287
+ explanation = select_all("EXPLAIN #{sql}").map{|v| v["QUERY PLAN"] }.join("\n")
288
+ raise ActiveRecord::Rollback
289
+ end
290
+
291
+ explanation
292
+ end
293
+
294
+ def explain_safe?
295
+ begin
296
+ select_all("SELECT 1; SELECT 1")
297
+ false
298
+ rescue ActiveRecord::StatementInvalid
299
+ true
300
+ end
301
+ end
302
+
188
303
  def select_all(sql)
189
304
  # squish for logs
190
- connection.select_all(sql.squish).to_a
305
+ connection.select_all(squish(sql)).to_a
306
+ end
307
+
308
+ def execute(sql)
309
+ connection.execute(sql)
191
310
  end
192
311
 
193
312
  def connection
194
313
  @connection ||= Connection.connection
195
314
  end
196
315
 
316
+ # from ActiveSupport
317
+ def squish(str)
318
+ str.to_s.gsub(/\A[[:space:]]+/, '').gsub(/[[:space:]]+\z/, '').gsub(/[[:space:]]+/, ' ')
319
+ end
320
+
197
321
  end
198
322
  end
@@ -1,3 +1,3 @@
1
1
  module PgHero
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/pghero.gemspec CHANGED
@@ -18,6 +18,10 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency "pg"
22
+ spec.add_dependency "activerecord"
23
+
21
24
  spec.add_development_dependency "bundler", "~> 1.6"
22
25
  spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "minitest"
23
27
  end
@@ -0,0 +1,20 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestPgHero < Minitest::Test
4
+
5
+ def setup
6
+ User.delete_all
7
+ end
8
+
9
+ def test_explain
10
+ User.create!
11
+ PgHero.explain("ANALYZE DELETE FROM users")
12
+ assert_equal 1, User.count
13
+ end
14
+
15
+ def test_explain_multiple_statements
16
+ User.create!
17
+ assert_raises(ActiveRecord::StatementInvalid){ PgHero.explain("ANALYZE DELETE FROM users; DELETE FROM users; COMMIT") }
18
+ end
19
+
20
+ end
@@ -0,0 +1,16 @@
1
+ require "bundler/setup"
2
+ Bundler.require(:default)
3
+ require "minitest/autorun"
4
+ require "minitest/pride"
5
+
6
+ # for Minitest < 5
7
+ Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
8
+
9
+ ActiveRecord::Base.establish_connection adapter: "postgresql", database: "pghero_test"
10
+
11
+ ActiveRecord::Migration.create_table :users, force: true do |t|
12
+ # just id
13
+ end
14
+
15
+ class User < ActiveRecord::Base
16
+ end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pghero
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-23 00:00:00.000000000 Z
11
+ date: 2014-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pg
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: bundler
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +66,20 @@ dependencies:
38
66
  - - ">="
39
67
  - !ruby/object:Gem::Version
40
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
41
83
  description: Database insights made easy
42
84
  email:
43
85
  - andrew@chartkick.com
@@ -54,15 +96,23 @@ files:
54
96
  - app/controllers/pg_hero/home_controller.rb
55
97
  - app/views/layouts/pg_hero/application.html.erb
56
98
  - app/views/pg_hero/home/_queries_table.html.erb
99
+ - app/views/pg_hero/home/_query_stats_table.html.erb
100
+ - app/views/pg_hero/home/explain.html.erb
57
101
  - app/views/pg_hero/home/index.html.erb
58
102
  - app/views/pg_hero/home/indexes.html.erb
59
103
  - app/views/pg_hero/home/queries.html.erb
104
+ - app/views/pg_hero/home/query_stats.html.erb
60
105
  - app/views/pg_hero/home/space.html.erb
61
106
  - config/routes.rb
107
+ - gemfiles/activerecord31.gemfile
108
+ - gemfiles/activerecord32.gemfile
109
+ - gemfiles/activerecord40.gemfile
62
110
  - lib/pghero.rb
63
111
  - lib/pghero/engine.rb
64
112
  - lib/pghero/version.rb
65
113
  - pghero.gemspec
114
+ - test/pghero_test.rb
115
+ - test/test_helper.rb
66
116
  homepage: https://github.com/ankane/pghero
67
117
  licenses:
68
118
  - MIT
@@ -87,5 +137,7 @@ rubygems_version: 2.2.2
87
137
  signing_key:
88
138
  specification_version: 4
89
139
  summary: Database insights made easy
90
- test_files: []
140
+ test_files:
141
+ - test/pghero_test.rb
142
+ - test/test_helper.rb
91
143
  has_rdoc: