peekdb 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .DS_Store
2
+ pkg
3
+ doc
4
+ *.gem
5
+ *.dot
6
+ *.pdf
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create ruby-1.9.2-p290@peekdb
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'pg'
4
+ gem 'ruby-graphviz'
5
+ gem 'rspec'
6
+ gem 'shoulda'
data/Gemfile.lock ADDED
@@ -0,0 +1,28 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ pg (0.13.2)
6
+ rspec (2.9.0)
7
+ rspec-core (~> 2.9.0)
8
+ rspec-expectations (~> 2.9.0)
9
+ rspec-mocks (~> 2.9.0)
10
+ rspec-core (2.9.0)
11
+ rspec-expectations (2.9.1)
12
+ diff-lcs (~> 1.1.3)
13
+ rspec-mocks (2.9.0)
14
+ ruby-graphviz (1.0.5)
15
+ shoulda (3.0.1)
16
+ shoulda-context (~> 1.0.0)
17
+ shoulda-matchers (~> 1.0.0)
18
+ shoulda-context (1.0.0)
19
+ shoulda-matchers (1.0.0)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ pg
26
+ rspec
27
+ ruby-graphviz
28
+ shoulda
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # PeekDB - Database Quick View
2
+
3
+ A command line utility that builds a simple force-directed ER diagram for a database. The output can be .png or .dot formats saved in the current directory. Dot files are great for reuse within Omnigraffle.
4
+
5
+
6
+ Installation
7
+ -------
8
+ gem install peek
9
+
10
+ Usage
11
+ -------
12
+ peekdb -n [database-name] -t [psql | mysql | sqlite ] -f [png | dot]
13
+
14
+ Todo
15
+ -------
16
+ * Add support for SQLite and MySQL
17
+ * Add support for extraction of views
18
+ * Add support for extraction of column names
19
+
20
+ License
21
+ -------
22
+
23
+ MIT License. Copyright 2012 Ennova.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :default => :spec
8
+ task :test => :spec
data/bin/peekdb ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/peekdb'
3
+
4
+ begin
5
+ PeekDB.new(ARGV).run
6
+ rescue
7
+ exit(1)
8
+ else
9
+ exit(0)
10
+ end
@@ -0,0 +1,36 @@
1
+ class Database
2
+
3
+ attr_reader :name, :connection, :tables, :relations
4
+
5
+ def initialize(name)
6
+ @name = name
7
+
8
+ begin
9
+ open_connection(@name)
10
+ puts "... Inspecting database #{@name}"
11
+
12
+ find_tables
13
+ puts "... Found #{@tables.size} tables"
14
+
15
+ find_relations
16
+ puts "... Found #{@relations.size} relations"
17
+ rescue Exception => e
18
+ puts "... Error #{e}"
19
+ exit(1)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def open_connection(database_name)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def find_tables
30
+ raise NotImplementedError
31
+ end
32
+
33
+ def find_relations
34
+ raise NotImplementedError
35
+ end
36
+ end
@@ -0,0 +1,2 @@
1
+ class DatabaseMySQL < Database
2
+ end
@@ -0,0 +1,57 @@
1
+ class DatabasePSQL < Database
2
+
3
+ attr_reader :name, :connection, :tables, :relations
4
+
5
+ private
6
+
7
+ def open_connection(database_name)
8
+ @connection = PGconn.open(:dbname => @name)
9
+ end
10
+
11
+ # Reference from PostgreSQL:
12
+ # \d information_schema.tables
13
+ # Column | Type | Modifiers
14
+ # -----------------------------+-----------------------------------+-----------
15
+ # table_catalog | information_schema.sql_identifier |
16
+ # table_schema | information_schema.sql_identifier |
17
+ # table_name | information_schema.sql_identifier |
18
+ # table_type | information_schema.character_data |
19
+ # self_referencing_column_name | information_schema.sql_identifier |
20
+ # reference_generation | information_schema.character_data |
21
+ # user_defined_type_catalog | information_schema.sql_identifier |
22
+ # user_defined_type_schema | information_schema.sql_identifier |
23
+ # user_defined_type_name | information_schema.sql_identifier |
24
+ # is_insertable_into | information_schema.yes_or_no |
25
+ # is_typed | information_schema.yes_or_no |
26
+ # commit_action | information_schema.character_data |
27
+ def find_tables
28
+ sql = <<-eos
29
+ SELECT table_name
30
+ FROM information_schema.tables
31
+ WHERE table_type = 'BASE TABLE'
32
+ AND table_schema = 'public'
33
+ eos
34
+ @tables = @connection.exec(sql).values.flatten
35
+ end
36
+
37
+ # Reference from PostgreSQL:
38
+ # [0] constraint_name
39
+ # [1] table_name
40
+ # [2] column_name
41
+ # [3] foreign_table_name
42
+ # [4] foreign_column_name
43
+ def find_relations
44
+ sql = <<-eos
45
+ SELECT
46
+ tc.constraint_name, tc.table_name, kcu.column_name,
47
+ ccu.table_name AS foreign_table_name,
48
+ ccu.column_name AS foreign_column_name
49
+ FROM
50
+ information_schema.table_constraints AS tc
51
+ JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
52
+ JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
53
+ WHERE constraint_type = 'FOREIGN KEY'
54
+ eos
55
+ @relations = @connection.exec(sql).values
56
+ end
57
+ end
@@ -0,0 +1,2 @@
1
+ class DatabaseSQLite < Database
2
+ end
@@ -0,0 +1,73 @@
1
+ class Graph
2
+
3
+ attr_reader :dwg
4
+
5
+ def initialize
6
+ @dwg = GraphViz.new(:G, :type => :digraph)
7
+ end
8
+
9
+ def build(name, tables, relations)
10
+ config_graph(name)
11
+ config_nodes
12
+ config_edges
13
+
14
+ tables.each do |table|
15
+ @dwg.add_nodes(table)
16
+ end
17
+
18
+ relations.each do |relation|
19
+ @dwg.add_edges(relation[3], relation[1])
20
+ end
21
+ end
22
+
23
+ def output(name, format)
24
+ case format
25
+ when :pdf, nil
26
+ filename = "#{name}.pdf"
27
+ @dwg.output(:pdf => "#{filename}")
28
+ puts "... Writing output #{filename}"
29
+ when :dot
30
+ filename = "#{name}.dot"
31
+ @dwg.output(:dot => filename)
32
+ puts "... Writing output #{filename}"
33
+ else
34
+ raise ArgumentError.new("Unknown output format #{format}")
35
+ exit(1)
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def config_graph(name)
42
+ title = name.split('_').each{|s| s.capitalize!}.join(' ')
43
+ date = Time.now
44
+ @dwg.graph[:labelloc] = "t"
45
+ @dwg.graph[:label] = "#{title} Database\nGenerated #{date}"
46
+ @dwg.graph[:fontname] = "Helvetica"
47
+ @dwg.graph[:fontcolor] = "#666666"
48
+ @dwg.graph[:fontsize] = "24"
49
+ end
50
+
51
+ def config_nodes
52
+ @dwg.node[:color] = "#222222"
53
+ @dwg.node[:style] = "filled"
54
+ @dwg.node[:shape] = "box"
55
+ @dwg.node[:penwidth] = "1"
56
+ @dwg.node[:fontname] = "Helvetica"
57
+ @dwg.node[:fillcolor] = "#eeeeee"
58
+ @dwg.node[:fontcolor] = "#333333"
59
+ @dwg.node[:margin] = "0.05"
60
+ @dwg.node[:fontsize] = "12"
61
+ end
62
+
63
+ def config_edges
64
+ @dwg.edge[:color] = "#666666"
65
+ @dwg.edge[:weight] = "1"
66
+ @dwg.edge[:fontsize] = "10"
67
+ @dwg.edge[:fontcolor] = "#444444"
68
+ @dwg.edge[:fontname] = "Helvetica"
69
+ @dwg.edge[:dir] = "forward"
70
+ @dwg.edge[:arrowsize] = "0.5"
71
+ @dwg.edge[:arrowhead] = "crow"
72
+ end
73
+ end
data/lib/peekdb.rb ADDED
@@ -0,0 +1,97 @@
1
+ require 'pg'
2
+ require 'graphviz'
3
+ require 'optparse'
4
+ require 'ostruct'
5
+ require 'pry'
6
+
7
+ require_relative 'peekdb/graph'
8
+ require_relative 'peekdb/database'
9
+ require_relative 'peekdb/database_psql'
10
+ require_relative 'peekdb/database_mysql'
11
+ require_relative 'peekdb/database_sqlite'
12
+
13
+ class PeekDB
14
+
15
+ def initialize(argv)
16
+ puts "PeekDB:"
17
+ begin
18
+ parse_options(argv)
19
+ rescue Exception => e
20
+ puts "... Error FATAL: #{e}"
21
+ exit(1)
22
+ end
23
+ end
24
+
25
+ def parse_options(args)
26
+ @options = OpenStruct.new
27
+
28
+ opts = OptionParser.new do |opts|
29
+ opts.banner = "Usage: peekdb [options]"
30
+ opts.program_name = "PeekDB"
31
+ opts.separator ""
32
+ opts.separator "Specific options:"
33
+
34
+ opts.on("-n", "--name NAME", "Name of database - required") do |name|
35
+ @options.name = name
36
+ end
37
+
38
+ opts.on("-t", "--type [TYPE]", [:psql, :mysql, :sqlite], "Type of database (psql, mysql, sqlite) - required") do |type|
39
+ @options.type = type
40
+ end
41
+
42
+ opts.on("-f", "--format [FORMAT]", [:pdf, :dot], "Format of output file (pdf, dot) - default pdf") do |format|
43
+ @options.format = format
44
+ end
45
+ end
46
+ opts.parse!(args)
47
+ end
48
+
49
+ def run
50
+ if @options.name && @options.name
51
+ case @options.type
52
+ when :psql
53
+ db = DatabasePSQL.new @options.name
54
+ when :mysql
55
+ db = DatabaseMySQL.new @options.name
56
+ when :sqlite
57
+ db = DatabaseSQLite.new @options.name
58
+ else
59
+ puts "... Error FATAL: Unknown database type #{@options.type}"
60
+ exit(1)
61
+ end
62
+
63
+ graph = Graph.new
64
+ graph.build(db.name, db.tables, db.relations)
65
+ graph.output(db.name, @options.format)
66
+ else
67
+ puts '... Error FATAL: missing arguments'
68
+ exit(1)
69
+ end
70
+ end
71
+
72
+ def self.usage
73
+ puts <<-EOT
74
+ Usage: rails COMMAND [ARGS]
75
+
76
+ The most common rails commands are:
77
+ generate Generate new code (short-cut alias: "g")
78
+ console Start the Rails console (short-cut alias: "c")
79
+ server Start the Rails server (short-cut alias: "s")
80
+ dbconsole Start a console for the database specified in config/database.yml
81
+ (short-cut alias: "db")
82
+ new Create a new Rails application. "rails new my_app" creates a
83
+ new application called MyApp in "./my_app"
84
+
85
+ In addition to those, there are:
86
+ application Generate the Rails application code
87
+ destroy Undo code generated with "generate" (short-cut alias: "d")
88
+ benchmarker See how fast a piece of code runs
89
+ profiler Get profile information from a piece of code
90
+ plugin new Generates skeleton for developing a Rails plugin
91
+ runner Run a piece of code in the application environment (short-cut alias: "r")
92
+
93
+ All commands can be run with -h (or --help) for more information.
94
+ EOT
95
+ end
96
+
97
+ end
data/peekdb.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = "peekdb"
3
+ gem.version = "0.2.0"
4
+ gem.platform = Gem::Platform::RUBY
5
+ gem.authors = ["Adrian Smith"]
6
+ gem.email = ["adrian.smith@ennova.com.au"]
7
+ gem.homepage = "https://github.com/AdrianSmith/peekdb"
8
+ gem.summary = %q{PeekDB generates a quick view of the tables and relationships in a database}
9
+ gem.description = %q{PeekDB is a command line utility that builds a simple force-directed ER diagram for a database. The output can be .png or .dot formats.}
10
+ gem.license = 'MIT'
11
+
12
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
13
+ gem.files = `git ls-files`.split("\n")
14
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+
16
+ gem.add_dependency 'ruby-graphviz'
17
+ gem.add_dependency 'pg'
18
+
19
+ gem.add_development_dependency 'rake'
20
+ gem.add_development_dependency 'rspec'
21
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../spec_helper.rb'
2
+
3
+ describe Graph do
4
+ before do
5
+ @tables = ['book', 'author', 'format']
6
+ @relations = [
7
+ ['author-book', 'author', 'id', 'book', 'id'],
8
+ ['book-format', 'book', 'id', 'format', 'id']
9
+ ]
10
+ end
11
+
12
+ it "should create a new graph using database table and relation data" do
13
+ graph = Graph.new
14
+ graph.build('test', @tables, @relations)
15
+ graph.dwg.should be_not_nil
16
+ end
17
+
18
+ end
@@ -0,0 +1,2 @@
1
+ require 'rspec'
2
+ require 'peekdb'
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: peekdb
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.2.0
6
+ platform: ruby
7
+ authors:
8
+ - Adrian Smith
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-04-29 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ruby-graphviz
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: pg
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :runtime
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: rake
39
+ prerelease: false
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec
50
+ prerelease: false
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :development
58
+ version_requirements: *id004
59
+ description: PeekDB is a command line utility that builds a simple force-directed ER diagram for a database. The output can be .png or .dot formats.
60
+ email:
61
+ - adrian.smith@ennova.com.au
62
+ executables:
63
+ - peekdb
64
+ extensions: []
65
+
66
+ extra_rdoc_files: []
67
+
68
+ files:
69
+ - .gitignore
70
+ - .rvmrc
71
+ - Gemfile
72
+ - Gemfile.lock
73
+ - README.md
74
+ - Rakefile
75
+ - bin/peekdb
76
+ - lib/peekdb.rb
77
+ - lib/peekdb/database.rb
78
+ - lib/peekdb/database_mysql.rb
79
+ - lib/peekdb/database_psql.rb
80
+ - lib/peekdb/database_sqlite.rb
81
+ - lib/peekdb/graph.rb
82
+ - peekdb.gemspec
83
+ - spec/peekdb/graph_spec.rb
84
+ - spec/spec_helper.rb
85
+ homepage: https://github.com/AdrianSmith/peekdb
86
+ licenses:
87
+ - MIT
88
+ post_install_message:
89
+ rdoc_options: []
90
+
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: "0"
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: "0"
105
+ requirements: []
106
+
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.23
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: PeekDB generates a quick view of the tables and relationships in a database
112
+ test_files:
113
+ - spec/peekdb/graph_spec.rb
114
+ - spec/spec_helper.rb