elefant 0.0.1

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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +13 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +65 -0
  9. data/Rakefile +8 -0
  10. data/bin/elefant-web +3 -0
  11. data/config.ru +4 -0
  12. data/elefant.gemspec +32 -0
  13. data/lib/elefant.rb +22 -0
  14. data/lib/elefant/connection_adapter.rb +122 -0
  15. data/lib/elefant/postgres/size_queries.rb +31 -0
  16. data/lib/elefant/postgres/stat_queries.rb +91 -0
  17. data/lib/elefant/stats.rb +56 -0
  18. data/lib/elefant/version.rb +3 -0
  19. data/lib/elefant/web.rb +86 -0
  20. data/test/ar_connection_test.rb +55 -0
  21. data/test/config.yml.example +4 -0
  22. data/test/fixtures/teardown.sql +4 -0
  23. data/test/fixtures/test_models.sql +11 -0
  24. data/test/pg_connection_test.rb +12 -0
  25. data/test/stats_test.rb +32 -0
  26. data/test/test_helper.rb +37 -0
  27. data/test/webapp_test.rb +46 -0
  28. data/watchr.rb +34 -0
  29. data/web/locales/en.yml +33 -0
  30. data/web/public/css/_fonts.scss +36 -0
  31. data/web/public/css/_lists.scss +16 -0
  32. data/web/public/css/_nav.scss +75 -0
  33. data/web/public/css/_normalize.scss +427 -0
  34. data/web/public/css/_skeleton.scss +418 -0
  35. data/web/public/css/_tables.scss +36 -0
  36. data/web/public/css/_typography.scss +24 -0
  37. data/web/public/css/_variables.scss +2 -0
  38. data/web/public/css/elefant.css +13 -0
  39. data/web/public/css/elefant.css.map +7 -0
  40. data/web/public/css/elefant.scss +45 -0
  41. data/web/public/fonts/ClearSans-Bold.eot +0 -0
  42. data/web/public/fonts/ClearSans-Bold.svg +22646 -0
  43. data/web/public/fonts/ClearSans-Bold.ttf +0 -0
  44. data/web/public/fonts/ClearSans-Bold.woff +0 -0
  45. data/web/public/fonts/ClearSans-Light.eot +0 -0
  46. data/web/public/fonts/ClearSans-Light.svg +22411 -0
  47. data/web/public/fonts/ClearSans-Light.ttf +0 -0
  48. data/web/public/fonts/ClearSans-Light.woff +0 -0
  49. data/web/public/fonts/LICENSE-2.0.txt +202 -0
  50. data/web/public/img/postgresql.ico +0 -0
  51. data/web/public/img/postgresql_logo.svg +22 -0
  52. data/web/public/img/screenshot.png +0 -0
  53. data/web/public/js/zepto.min.js +2 -0
  54. data/web/views/activity.erb +27 -0
  55. data/web/views/indices.erb +12 -0
  56. data/web/views/layout.erb +58 -0
  57. data/web/views/partials/field_table.erb +18 -0
  58. data/web/views/partials/generic_table.erb +23 -0
  59. data/web/views/partials/list.erb +6 -0
  60. data/web/views/size.erb +23 -0
  61. data/web/views/summary.erb +18 -0
  62. data/web/views/tables.erb +5 -0
  63. metadata +255 -0
@@ -0,0 +1,56 @@
1
+ require "elefant/connection_adapter"
2
+ require "elefant/postgres/stat_queries"
3
+ require "elefant/postgres/size_queries"
4
+
5
+ module Elefant
6
+ class Stats
7
+ include Elefant::Postgres::StatQueries
8
+ include Elefant::Postgres::SizeQueries
9
+
10
+ def initialize
11
+ @connection = Elefant::ConnectionAdapter.new
12
+ end
13
+
14
+ def db_name
15
+ @connection.info[:db_name]
16
+ end
17
+
18
+ def version
19
+ @connection.info[:server_version]
20
+ end
21
+
22
+ def client_version
23
+ @connection.info[:client_version]
24
+ end
25
+
26
+ def get(name, params)
27
+ query(name, params)
28
+ end
29
+
30
+ def close!
31
+ @connection.disconnect
32
+ end
33
+
34
+ def self.check!
35
+ connection = Elefant::ConnectionAdapter.new
36
+ raise ArgumentError.new("Could not establish connection") unless connection.alive?
37
+ connection.disconnect
38
+ end
39
+
40
+ private
41
+
42
+ def exec(query, params = [])
43
+ @connection.execute(query, params)
44
+ end
45
+
46
+ def query(name, params)
47
+ method = name.to_sym
48
+
49
+ if respond_to?(method)
50
+ send(method, *params)
51
+ else
52
+ raise ArgumentError.new("Unknown Stats Query: #{name}")
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module Elefant
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,86 @@
1
+ require "sinatra"
2
+ require "sinatra/partial"
3
+ require "i18n"
4
+ require "i18n/backend/fallbacks"
5
+ require "elefant"
6
+
7
+ module Elefant
8
+ class Web < Sinatra::Base
9
+ register Sinatra::Partial
10
+
11
+ dir = File.expand_path(File.dirname(__FILE__) + "/../../web")
12
+
13
+ set :public_folder, "#{dir}/public"
14
+ set :views, "#{dir}/views"
15
+ set :root, "#{dir}/public"
16
+ set :locales, "#{dir}/locales"
17
+
18
+ set :partial_template_engine, :erb
19
+ set :layout_engine, :erb
20
+
21
+ configure do
22
+ Elefant::Stats.check!
23
+ I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
24
+ I18n.load_path = Dir[File.join(settings.locales, '*.yml')]
25
+ I18n.backend.load_translations
26
+ end
27
+
28
+ before do
29
+ @stats ||= Elefant::Stats.new
30
+ end
31
+
32
+ after do
33
+ @stats.close!
34
+ end
35
+
36
+ helpers do
37
+
38
+ def stats
39
+ @stats ||= Elefant::Stats.new
40
+ end
41
+
42
+ def get_stats(name, params = [])
43
+ stats.get(name, params)
44
+ end
45
+
46
+ def css_for(field)
47
+ I18n.t(field, scope: "css", default: "")
48
+ end
49
+
50
+ def t(*args, options)
51
+ options.merge!(locale: :en)
52
+ I18n.t(*args, options)
53
+ end
54
+
55
+ def link(name, target, params = {})
56
+ extra = params.any? ? "?" + params.map {|k, v| "#{k}=#{v}" }.join("&") : ""
57
+ css = target == current_path ? 'active' : ''
58
+ %Q{<a href="#{root_path}#{target}#{extra}" class="#{css}">#{name}</a>}
59
+ end
60
+
61
+ def root_path
62
+ %Q{#{env['SCRIPT_NAME']}/}
63
+ end
64
+
65
+ def current_path
66
+ @current_path ||= request.path_info.gsub(/^\//,'')
67
+ end
68
+ end
69
+
70
+ get "/" do
71
+ redirect "#{root_path}summary", 302
72
+ end
73
+
74
+ get "/:name" do
75
+ name = params[:name]
76
+ if %w(activity indices size summary tables).include?(name)
77
+ erb name.to_sym
78
+ else
79
+ not_found do
80
+ "Page not found! Sorry."
81
+ end
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,55 @@
1
+ require "test_helper"
2
+
3
+ describe "ActiveRecord being loaded" do
4
+
5
+ fork do
6
+ before do
7
+ @db_url = ENV.delete("DATABASE_URL")
8
+ config_file = File.expand_path("config.yml", File.dirname(__FILE__))
9
+
10
+ require "active_record"
11
+
12
+ unless File.exist?(config_file)
13
+ puts "Please copy test/config.yml to test/config.yml.example and edit accordingly"
14
+ abort
15
+ end
16
+
17
+ ActiveRecord::Base.establish_connection(YAML::load(File.open(config_file)))
18
+ ActiveRecord::Base.connection.execute("SELECT 1")
19
+
20
+ @old_ar_config_value = Elefant.configuration.disable_ar
21
+ end
22
+
23
+ after do
24
+ ENV["DATABASE_URL"] = @db_url
25
+
26
+ Elefant.configure do |c|
27
+ c.disable_ar = @old_ar_config_value
28
+ end
29
+ end
30
+
31
+ it "connects to the test database using AR" do
32
+ connection = Elefant::ConnectionAdapter.new
33
+
34
+ assert connection.alive?, "Connection must be alive"
35
+ assert connection.active_record?, "Connection must be active record"
36
+ end
37
+
38
+ it "does not use active record when disabled in the configuration" do
39
+ Elefant.configure do |c|
40
+ c.disable_ar = true
41
+ end
42
+
43
+ assert_raises ArgumentError do
44
+ Elefant::ConnectionAdapter.new
45
+ end
46
+ end
47
+ end
48
+
49
+ it "runs the ar specs successfully" do
50
+ Process.wait
51
+ exit_status = $?.to_i
52
+
53
+ assert_equal 0, exit_status
54
+ end
55
+ end
@@ -0,0 +1,4 @@
1
+ adapter: postgresql
2
+ database: elefant_test
3
+ user: postgres
4
+ password:
@@ -0,0 +1,4 @@
1
+ -- SET client_min_messages TO WARNING;
2
+
3
+ drop table if exists elefant_test_models;
4
+ drop sequence if exists elefant_id_seq;
@@ -0,0 +1,11 @@
1
+ CREATE SEQUENCE elefant_id_seq;
2
+
3
+ CREATE TABLE elefant_test_models (
4
+ id integer not null default nextval('elefant_id_seq'::regclass) PRIMARY KEY,
5
+ name character varying(255),
6
+ number integer,
7
+ created_at timestamp without time zone,
8
+ updated_at timestamp without time zone
9
+ );
10
+
11
+ CREATE INDEX elefant_index ON elefant_test_models USING btree (name);
@@ -0,0 +1,12 @@
1
+ require "test_helper"
2
+
3
+ describe "when connecting via DATABASE_URL" do
4
+
5
+ it "connects to the test database using the pg driver directly" do
6
+ connection = Elefant::ConnectionAdapter.new
7
+
8
+ assert connection.alive?, "Connection must be alive"
9
+ refute connection.active_record?, "Connection must not be active record"
10
+ end
11
+ end
12
+
@@ -0,0 +1,32 @@
1
+ require "test_helper"
2
+ require "elefant/stats"
3
+
4
+ class StatsTest < PGTest
5
+
6
+ def setup
7
+ super
8
+ @stats = Elefant::Stats.new
9
+ end
10
+
11
+ def test_retrieves_activity
12
+ result = @stats.get("activity", [])
13
+
14
+ assert_instance_of Array, result
15
+ refute_empty result
16
+ end
17
+
18
+ def test_retrieves_user_indices
19
+ result = @stats.get("user_indexes", [])
20
+
21
+ assert_instance_of Array, result
22
+ refute_empty result
23
+ assert_equal "elefant_test_models", result[0]["rel_name"]
24
+ end
25
+
26
+ def test_raises_for_unknown
27
+ assert_raises ArgumentError do
28
+ @stats.get("totallyweirdunknownstatname")
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,37 @@
1
+ $: << File.expand_path("lib")
2
+ $: << File.expand_path("test")
3
+
4
+ require "bundler"
5
+ Bundler.setup :default, :test
6
+
7
+ ENV["DATABASE_URL"] ||= "postgres:///elefant_test"
8
+
9
+ require "elefant"
10
+ require "minitest/autorun"
11
+
12
+ Elefant.configure do |c|
13
+ c.disable_ar = false
14
+ end
15
+
16
+ class PGTest < Minitest::Test
17
+
18
+ def setup
19
+ init_db
20
+ end
21
+
22
+ def teardown
23
+ reset_db
24
+ end
25
+
26
+ def init_db
27
+ c = Elefant::ConnectionAdapter.new
28
+ c.execute(File.read('./test/fixtures/test_models.sql'))
29
+ c.disconnect
30
+ end
31
+
32
+ def reset_db
33
+ c = Elefant::ConnectionAdapter.new
34
+ c.execute(File.read('./test/fixtures/teardown.sql'))
35
+ c.disconnect
36
+ end
37
+ end
@@ -0,0 +1,46 @@
1
+ require "test_helper"
2
+ require "rack/test"
3
+ require "elefant/web"
4
+
5
+ ENV["RACK_ENV"] = "test"
6
+
7
+ describe "the elefant web interface" do
8
+ include Rack::Test::Methods
9
+
10
+ def app
11
+ Elefant::Web
12
+ end
13
+
14
+ it "redirects to the summary on the index page" do
15
+ get "/"
16
+
17
+ assert last_response.redirect?
18
+ assert last_response.location.match("/summary")
19
+ end
20
+
21
+ it "shows a database summary" do
22
+ get "/summary"
23
+ assert last_response.ok?
24
+ end
25
+
26
+ it "shows activity info" do
27
+ get "/activity"
28
+ assert last_response.ok?
29
+ end
30
+
31
+ it "shows info about indices" do
32
+ get "/indices"
33
+ assert last_response.ok?
34
+ end
35
+
36
+ it "shows table info" do
37
+ get "/tables"
38
+ assert last_response.ok?
39
+ end
40
+
41
+ it "shows size info" do
42
+ get "/size"
43
+ assert last_response.ok?
44
+ end
45
+
46
+ end
@@ -0,0 +1,34 @@
1
+ # Run me with:
2
+ # $ watchr watchr.rb
3
+
4
+ # --------------------------------------------------
5
+ # Rules
6
+ # --------------------------------------------------
7
+ watch( '^test.*/*_test.*\.rb' ) { rake } # or run specific one { |m| ruby m[0] }
8
+ watch( '^lib/(.*)\.rb' ) { rake } # { |m| ruby "test/test_#{m[1]}.rb" }
9
+ watch( '^test/test_helper\.rb' ) { rake }
10
+ watch( '^web/views/(.*)\.(.*)' ) { rake }
11
+ watch( '^web/public/css/(.*)\.scss' ) { sass }
12
+
13
+ # --------------------------------------------------
14
+ # Signal Handling
15
+ # --------------------------------------------------
16
+ Signal.trap('QUIT') { rake } # Ctrl-\
17
+ Signal.trap('INT' ) { abort("\n") } # Ctrl-C
18
+
19
+ # --------------------------------------------------
20
+ # Helpers
21
+ # --------------------------------------------------
22
+ def rake
23
+ run "clear"
24
+ run "bundle exec rake"
25
+ end
26
+
27
+ def sass
28
+ run "bundle exec sass --style compressed --scss -I web/public/css/ web/public/css/elefant.scss web/public/css/elefant.css"
29
+ end
30
+
31
+ def run( cmd )
32
+ puts cmd
33
+ system cmd
34
+ end
@@ -0,0 +1,33 @@
1
+ en:
2
+ db:
3
+ db_name: "database"
4
+ db_time: "time"
5
+ dbsize: "total size"
6
+ num_rels: "total relations"
7
+ commits: "commits"
8
+ rollbks: "rollbacks"
9
+ blksrd: "blocks read"
10
+ blkshit: "blocks hit"
11
+ bkends: "backends"
12
+ seqscan: "sequential scans"
13
+ seqtprd: "sequential tupels read"
14
+ idxscn: "index scans"
15
+ idxtrd: "index tupel fetches"
16
+ ins: "inserts"
17
+ upd: "updates"
18
+ del: "deletes"
19
+ locks: "locks"
20
+ activeq: "max active queries"
21
+ kind:
22
+ i: "index"
23
+ t: "toast"
24
+ r: "table"
25
+ S: "sequence"
26
+ css:
27
+ idx_scn: "numeric"
28
+ idx_tup_rd: "numeric"
29
+ idx_tup_ftch: "numeric"
30
+ heap_blks_rd: "numeric"
31
+ heap_blks_ht: "numeric"
32
+ idx_blks_rd: "numeric"
33
+ idx_blks_ht: "numeric"
@@ -0,0 +1,36 @@
1
+ /*!
2
+ * Clear Sans Webfont
3
+ *
4
+ * Webfont conversion of the Clear Sans typeface, designed by the
5
+ * Intel Open Source Technology Center <https://01.org/clear-sans>
6
+ *
7
+ * Original font file released under the Apache 2.0 License
8
+ * <http://www.apache.org/licenses/LICENSE-2.0.html>
9
+ *
10
+ * Webfont version by Resi Respati <resir014@gmail.com>
11
+ * Released under the MIT License.
12
+ */
13
+
14
+ @font-face {
15
+ font-family: 'Clear Sans';
16
+ font-style: normal;
17
+ font-weight: 300;
18
+ src: local('ClearSans-Light'), local('Clear Sans Light'),
19
+ url('../fonts/ClearSans-Light.eot'),
20
+ url('../fonts/ClearSans-Light.eot?#iefix') format('embedded-opentype'),
21
+ url('../fonts/ClearSans-Light.woff') format('woff'),
22
+ url('../fonts/ClearSans-Light.ttf') format('truetype'),
23
+ url('../fonts/ClearSans-Light.svg') format('svg');
24
+ }
25
+
26
+ @font-face {
27
+ font-family: 'Clear Sans';
28
+ font-style: normal;
29
+ font-weight: 700;
30
+ src: local('ClearSans-Bold'), local('Clear Sans Bold'),
31
+ url('../fonts/ClearSans-Bold.eot'),
32
+ url('../fonts/ClearSans-Bold.eot?#iefix') format('embedded-opentype'),
33
+ url('../fonts/ClearSans-Bold.woff') format('woff'),
34
+ url('../fonts/ClearSans-Bold.ttf') format('truetype'),
35
+ url('../fonts/ClearSans-Bold.svg') format('svg');
36
+ }