peekdb 0.2.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.
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