gadget 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f6feadef1150080b07735c8d7d83516d87bceea9
4
+ data.tar.gz: 9c74000e08c87120b794c6871f8126198c9d248b
5
+ SHA512:
6
+ metadata.gz: e0ce84932a8c25607920db5a7111e8a3e37c70fc4eb6c486c467d0b7fd488246f880a0853e9bc226f100f750db631197f5ae7290fb1d266fcba30df01f8e7985
7
+ data.tar.gz: 1a6fc7958a2c250af87035cb7619270db989462a02c052b6db2e9bfd1ec3fd12fefe7154415bf6150e9cd511848dd0816f4c49fdafafd8b7a4aad2cd75dd5326
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ /.ruby-gemset
20
+ /.ruby-version
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Craig S. Cottingham
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,51 @@
1
+ # Gadget
2
+
3
+ Some methods for getting metadata and other deep details from a PostgreSQL database.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'gadget'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install gadget
18
+
19
+ ## Usage
20
+
21
+ `#tables(conn)`
22
+
23
+ Returns a list of all tables in the schema reachable through `conn`.
24
+
25
+ `#columns(conn, tablename=nil)`
26
+
27
+ Returns a list of all columns in the schema reachable through `conn`.
28
+ If `tablename` is given, returns the columns in only that table.
29
+
30
+ `#foreign_keys(conn, tablename=nil)`
31
+
32
+ Returns a list of all foreign keys in the schema reachable through `conn`.
33
+ If `tablename` is given, returns the foreign keys in only that table.
34
+
35
+ `#dependencies(conn)`
36
+
37
+ Returns a structure representing the dependencies between tables in the schema reachable through `conn`.
38
+ Table A is defined as dependent on table B if A contains a foreign key reference to B.
39
+
40
+ `#tables_in_dependency_order(conn)`
41
+
42
+ Returns a list of all tables in the schema reachable through `conn`, ordered such that any given table
43
+ appears later in the list than all of its dependencies.
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'gadget'
4
+
5
+ require 'pg'
6
+
7
+ dbname = ARGV.shift
8
+
9
+ conn = PG::Connection.open(:dbname => dbname)
10
+ tables = Gadget.tables_in_dependency_order(conn)
11
+ conn.close
12
+ p tables
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'gadget/version'
6
+
7
+ Gem::Specification.new do | spec |
8
+ spec.name = 'gadget'
9
+ spec.version = Gadget::VERSION
10
+ spec.authors = [ 'Craig S. Cottingham' ]
11
+ spec.email = [ 'craig.cottingham@gmail.com' ]
12
+ spec.summary = %q{Some methods for getting metadata and other deep details from a PostgreSQL database.}
13
+ spec.description = File.read(File.join(File.dirname(__FILE__), 'README.md'))
14
+ spec.homepage = ''
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = [ 'lib' ]
21
+
22
+ spec.add_dependency 'pg', '~> 0.17.0'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.3'
25
+ spec.add_development_dependency 'rake'
26
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+
3
+ require 'pg'
4
+ require 'tsort'
5
+
6
+ require 'gadget/version'
7
+
8
+ module Gadget
9
+
10
+ class TsortableHash < Hash
11
+ include ::TSort
12
+ alias tsort_each_node each_key
13
+ def tsort_each_child(node, &block)
14
+ (fetch(node) || []).each(&block)
15
+ end
16
+ end
17
+
18
+ def self.tables(conn)
19
+ rs = conn.exec("SELECT c.oid, t.tablename FROM pg_tables t INNER JOIN pg_class c ON c.relname=t.tablename WHERE t.schemaname='public' ORDER BY t.tablename")
20
+ tuples = rs.reduce({}) { | h, row | h[row['tablename']] = { :oid => row['oid'] }; h }
21
+ rs.clear
22
+ tuples
23
+ end
24
+
25
+ def self.columns(conn, tablename = nil)
26
+ sql = <<-END_OF_SQL
27
+ SELECT t.tablename, a.attname
28
+ FROM pg_attribute a
29
+ INNER JOIN pg_class c ON a.attrelid=c.oid
30
+ INNER JOIN pg_tables t ON c.relname=t.tablename
31
+ END_OF_SQL
32
+ if tablename.nil?
33
+ rs = conn.exec(sql)
34
+ else
35
+ sql += " WHERE t.tablename=$1"
36
+ rs = conn.exec_params(sql, [ tablename ])
37
+ end
38
+ # tuples = rs.map { | row | row }
39
+ tuples = rs.reduce({}) { | h, row | h[row['tablename']] ||= { :columns => [] }; h[row['tablename']][:columns] << row['attname']; h }
40
+ rs.clear
41
+ tuples
42
+ end
43
+
44
+ def self.foreign_keys(conn, tablename = nil)
45
+ sql = <<-END_OF_SQL
46
+ SELECT t1.tablename AS tablename, t2.tablename AS refname
47
+ FROM pg_constraint
48
+ INNER JOIN pg_class c1 ON pg_constraint.conrelid=c1.oid
49
+ INNER JOIN pg_tables t1 ON c1.relname=t1.tablename
50
+ INNER JOIN pg_class c2 ON pg_constraint.confrelid=c2.oid
51
+ INNER JOIN pg_tables t2 ON c2.relname=t2.tablename
52
+ WHERE t1.schemaname='public'
53
+ AND t2.schemaname='public'
54
+ AND pg_constraint.contype='f'
55
+ END_OF_SQL
56
+ if tablename.nil?
57
+ rs = conn.exec(sql)
58
+ else
59
+ sql += " AND t1.tablename=$1"
60
+ rs = conn.exec_params(sql, [ tablename ])
61
+ end
62
+ tuples = rs.reduce({}) { | h, row | h[row['tablename']] ||= { :refs => [] }; h[row['tablename']][:refs] << row['refname']; h }
63
+ rs.clear
64
+ tuples
65
+ end
66
+
67
+ def self.dependencies(conn)
68
+ tables = self.tables(conn)
69
+ foreign_keys = self.foreign_keys(conn)
70
+ dependencies = tables.reduce({}) do | h, (tablename, _) |
71
+ h[tablename] = []
72
+ refs = foreign_keys[tablename]
73
+ unless refs.nil?
74
+ refs[:refs].each { | ref | h[tablename] << ref }
75
+ end
76
+ h
77
+ end
78
+ end
79
+
80
+ def self.tables_in_dependency_order(conn)
81
+ self.dependencies(conn).reduce(TsortableHash.new) { | h, (k, v) | h[k] = v; h }.tsort
82
+ end
83
+
84
+ def self.dependency_graph(conn)
85
+ puts "digraph dependencies {"
86
+ self.dependencies(conn).each do | tablename, deps |
87
+ if deps.empty?
88
+ puts %Q<"#{tablename}">
89
+ else
90
+ deps.each { | dep | puts %Q|"#{tablename}" -> "#{dep}"| }
91
+ end
92
+ end
93
+ puts "}"
94
+ end
95
+
96
+ end
@@ -0,0 +1,3 @@
1
+ module Gadget
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gadget
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Craig S. Cottingham
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-27 00:00:00.000000000 Z
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.17.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.17.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: |
56
+ # Gadget
57
+
58
+ Some methods for getting metadata and other deep details from a PostgreSQL database.
59
+
60
+ ## Installation
61
+
62
+ Add this line to your application's Gemfile:
63
+
64
+ gem 'gadget'
65
+
66
+ And then execute:
67
+
68
+ $ bundle
69
+
70
+ Or install it yourself as:
71
+
72
+ $ gem install gadget
73
+
74
+ ## Usage
75
+
76
+ `#tables(conn)`
77
+
78
+ Returns a list of all tables in the schema reachable through `conn`.
79
+
80
+ `#columns(conn, tablename=nil)`
81
+
82
+ Returns a list of all columns in the schema reachable through `conn`.
83
+ If `tablename` is given, returns the columns in only that table.
84
+
85
+ `#foreign_keys(conn, tablename=nil)`
86
+
87
+ Returns a list of all foreign keys in the schema reachable through `conn`.
88
+ If `tablename` is given, returns the foreign keys in only that table.
89
+
90
+ `#dependencies(conn)`
91
+
92
+ Returns a structure representing the dependencies between tables in the schema reachable through `conn`.
93
+ Table A is defined as dependent on table B if A contains a foreign key reference to B.
94
+
95
+ `#tables_in_dependency_order(conn)`
96
+
97
+ Returns a list of all tables in the schema reachable through `conn`, ordered such that any given table
98
+ appears later in the list than all of its dependencies.
99
+
100
+ ## Contributing
101
+
102
+ 1. Fork it
103
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
104
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
105
+ 4. Push to the branch (`git push origin my-new-feature`)
106
+ 5. Create new Pull Request
107
+ email:
108
+ - craig.cottingham@gmail.com
109
+ executables:
110
+ - gadget
111
+ extensions: []
112
+ extra_rdoc_files: []
113
+ files:
114
+ - ".gitignore"
115
+ - Gemfile
116
+ - LICENSE.txt
117
+ - README.md
118
+ - Rakefile
119
+ - bin/gadget
120
+ - gadget.gemspec
121
+ - lib/gadget.rb
122
+ - lib/gadget/version.rb
123
+ homepage: ''
124
+ licenses:
125
+ - MIT
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.2.0.rc.1
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Some methods for getting metadata and other deep details from a PostgreSQL
147
+ database.
148
+ test_files: []