pg-doc 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +11 -0
- data/Appraisals +3 -0
- data/README.md +11 -10
- data/Rakefile +8 -1
- data/bin/rackup +21 -0
- data/bin/rake +21 -0
- data/config.ru +5 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/pg_1.x.gemfile +7 -0
- data/lib/pg/doc.rb +6 -1
- data/lib/pg/doc/engine.rb +281 -0
- data/lib/pg/doc/version.rb +1 -1
- data/pg-doc.gemspec +23 -13
- data/static/pg-doc.js +9 -0
- data/static/style.css +51 -0
- data/views/includes/left_menu.erb +47 -0
- data/views/includes/markdown.erb +32 -0
- data/views/index.erb +16 -0
- data/views/layout.erb +32 -0
- data/views/objects/function.erb +1 -0
- data/views/objects/schema.erb +5 -0
- data/views/objects/table.erb +94 -0
- data/views/objects/view.erb +1 -0
- metadata +122 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50222b07884abd889b86cc5cc645916e0c2f992a
|
4
|
+
data.tar.gz: b49449f6ff9e3ac829231db3e4c5290e35f209c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa70ad7c57bcff40d6bd6d0080760e354bdf13e3a9171ba7b69ef2278c3719b3f21f89cf7d4dd4d24ecf396c6fd6f78327d3f65f97be5605950b3484ce21df45
|
7
|
+
data.tar.gz: 6775c5074e41bf81874bbf483bbab6bcc3e7f111485f79f26d0bfba45e56ae20170b4b69fd550ece99c88ea405be02a08fc1ca731ccb51590ff53f334d393ebc
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Appraisals
ADDED
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# PG::Doc
|
2
2
|
|
3
|
-
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/pg-doc.svg)](http://badge.fury.io/rb/pg-doc)
|
4
|
+
[![Build Status](https://travis-ci.org/kenaniah/pg-doc.svg?branch=master)](http://travis-ci.org/kenaniah/pg-doc)
|
5
|
+
[![Dependency Status](https://gemnasium.com/kenaniah/pg-doc.svg)](https://gemnasium.com/kenaniah/pg-doc)
|
4
6
|
|
5
|
-
|
7
|
+
**This gem is under initial development. 0.1.0 will be the first release.**
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
@@ -20,15 +22,14 @@ Or install it yourself as:
|
|
20
22
|
|
21
23
|
$ gem install pg-doc
|
22
24
|
|
23
|
-
##
|
25
|
+
## To Do
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
27
|
+
- [ ] Document usage
|
28
|
+
- [ ] Document `mount PG::Doc::Web(url, options)`
|
29
|
+
- [ ] Document `:schema_filter` option
|
30
|
+
- [ ] Create and document caching to disk via marshal
|
31
|
+
- [ ] Create and document the markdown folder structure
|
32
|
+
- [ ] Write UI
|
32
33
|
|
33
34
|
## Contributing
|
34
35
|
|
data/Rakefile
CHANGED
data/bin/rackup
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rackup' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
12
|
+
load(bundle_binstub) if File.file?(bundle_binstub)
|
13
|
+
|
14
|
+
require "pathname"
|
15
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
16
|
+
Pathname.new(__FILE__).realpath)
|
17
|
+
|
18
|
+
require "rubygems"
|
19
|
+
require "bundler/setup"
|
20
|
+
|
21
|
+
load Gem.bin_path("rack", "rackup")
|
data/bin/rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
12
|
+
load(bundle_binstub) if File.file?(bundle_binstub)
|
13
|
+
|
14
|
+
require "pathname"
|
15
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
16
|
+
Pathname.new(__FILE__).realpath)
|
17
|
+
|
18
|
+
require "rubygems"
|
19
|
+
require "bundler/setup"
|
20
|
+
|
21
|
+
load Gem.bin_path("rake", "rake")
|
data/config.ru
ADDED
data/lib/pg/doc.rb
CHANGED
@@ -0,0 +1,281 @@
|
|
1
|
+
require "sinatra/base"
|
2
|
+
|
3
|
+
module PG
|
4
|
+
module Doc
|
5
|
+
|
6
|
+
# Returns an instantiated (and configured) rack app
|
7
|
+
def self.new connection, opts = {}
|
8
|
+
PG::Doc::Engine.new do |app|
|
9
|
+
app.setup connection, opts
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Engine < Sinatra::Base
|
14
|
+
|
15
|
+
set :public_folder, Proc.new { File.join(root, "../../../static") }
|
16
|
+
set :views, Proc.new { File.join(root, "../../../views") }
|
17
|
+
|
18
|
+
get '/' do
|
19
|
+
erb :index
|
20
|
+
end
|
21
|
+
|
22
|
+
get '/schemas/:schema(.:ext)?' do
|
23
|
+
object = @cache.dig :schemas, params["schema"]
|
24
|
+
pass unless object
|
25
|
+
erb :"objects/schema", locals: {object: object}
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/schemas/:schema/:object_type/:name(.:ext)?' do
|
29
|
+
object = @cache.dig :schemas, params["schema"], params["object_type"].to_sym, params["name"]
|
30
|
+
pass unless object
|
31
|
+
erb :"objects/#{params["object_type"].sub(/s$/, "")}", locals: {object: object}
|
32
|
+
end
|
33
|
+
|
34
|
+
# Defines helpers
|
35
|
+
helpers do
|
36
|
+
def render_markdown file
|
37
|
+
erb :"includes/markdown", locals: {file: File.join(@path_to_markdowns, file)} if @path_to_markdowns
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Initializes the internal state for this instance
|
42
|
+
def setup connection, opts
|
43
|
+
|
44
|
+
@conn = PG.connect connection
|
45
|
+
@path_to_markdowns = opts.fetch(:path_to_markdowns, nil)
|
46
|
+
@schema_filter = opts.fetch(:schema_filter, nil) || ->(field) {
|
47
|
+
<<~SQL
|
48
|
+
#{field} NOT ILIKE 'pg_%'
|
49
|
+
AND #{field} != 'information_schema'
|
50
|
+
SQL
|
51
|
+
}
|
52
|
+
|
53
|
+
@cache = {schemas: {}}
|
54
|
+
|
55
|
+
# Load schemas
|
56
|
+
_recordset = @conn.exec <<~SQL
|
57
|
+
SELECT
|
58
|
+
schema_name
|
59
|
+
FROM
|
60
|
+
information_schema.schemata
|
61
|
+
WHERE
|
62
|
+
#{@schema_filter.call :schema_name}
|
63
|
+
ORDER BY
|
64
|
+
1
|
65
|
+
SQL
|
66
|
+
_recordset.each_with_object(@cache){ |row, h|
|
67
|
+
h[:schemas][row["schema_name"]] = {
|
68
|
+
tables: {},
|
69
|
+
views: {},
|
70
|
+
functions: {}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
# Load tables
|
75
|
+
_recordset = @conn.exec <<~SQL
|
76
|
+
SELECT
|
77
|
+
table_schema,
|
78
|
+
table_name,
|
79
|
+
obj_description((table_schema || '.' || table_name)::regclass::oid, 'pg_class') as comment
|
80
|
+
FROM
|
81
|
+
information_schema.tables
|
82
|
+
WHERE
|
83
|
+
#{@schema_filter.call :table_schema}
|
84
|
+
ORDER BY
|
85
|
+
1, 2
|
86
|
+
SQL
|
87
|
+
_recordset.each_with_object(@cache){ |row, h|
|
88
|
+
h[:schemas][row["table_schema"]][:tables][row["table_name"]] = {
|
89
|
+
columns: [],
|
90
|
+
foreign_keys: {},
|
91
|
+
indexes: {},
|
92
|
+
comment: row["comment"]
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
# Load views
|
97
|
+
_recordset = @conn.exec <<~SQL
|
98
|
+
SELECT
|
99
|
+
table_schema,
|
100
|
+
table_name,
|
101
|
+
view_definition,
|
102
|
+
obj_description((table_schema || '.' || table_name)::regclass::oid, 'pg_class') as comment
|
103
|
+
FROM
|
104
|
+
information_schema.views
|
105
|
+
WHERE
|
106
|
+
#{@schema_filter.call :table_schema}
|
107
|
+
ORDER BY
|
108
|
+
1, 2
|
109
|
+
SQL
|
110
|
+
_recordset.each_with_object(@cache){ |row, h|
|
111
|
+
h[:schemas][row["table_schema"]][:views][row["table_name"]] = {
|
112
|
+
view_definition: row["view_definition"],
|
113
|
+
columns: [],
|
114
|
+
comment: row["comment"]
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
# Load columns
|
119
|
+
_recordset = @conn.exec <<~SQL
|
120
|
+
SELECT
|
121
|
+
c.table_schema,
|
122
|
+
c.table_name,
|
123
|
+
c.column_name,
|
124
|
+
c.ordinal_position,
|
125
|
+
c.data_type,
|
126
|
+
c.is_nullable,
|
127
|
+
c.column_default,
|
128
|
+
col_description((c.table_schema || '.' || c.table_name)::regclass::oid, c.ordinal_position) as comment,
|
129
|
+
t.table_type
|
130
|
+
FROM
|
131
|
+
information_schema.columns c
|
132
|
+
JOIN information_schema.tables t USING (table_catalog, table_schema, table_name)
|
133
|
+
WHERE
|
134
|
+
#{@schema_filter.call :table_schema}
|
135
|
+
ORDER BY
|
136
|
+
1, 2, 4
|
137
|
+
SQL
|
138
|
+
_recordset.each_with_object(@cache){ |row, h|
|
139
|
+
type = row.delete("table_type") == "VIEW" ? :views : :tables
|
140
|
+
schema = row.delete "table_schema"
|
141
|
+
name = row.delete "table_name"
|
142
|
+
h[:schemas][schema][type][name][:columns] << row
|
143
|
+
}
|
144
|
+
|
145
|
+
# Load functions (note: this currently does not support overloaded functions)
|
146
|
+
_recordset = @conn.exec <<~SQL
|
147
|
+
SELECT
|
148
|
+
routine_schema,
|
149
|
+
routine_name,
|
150
|
+
routine_definition,
|
151
|
+
external_language,
|
152
|
+
pg_get_function_identity_arguments((routine_schema || '.' || routine_name)::regproc) as arguments,
|
153
|
+
obj_description((routine_schema || '.' || routine_name)::regproc::oid, 'pg_proc') as comment
|
154
|
+
FROM
|
155
|
+
information_schema.routines
|
156
|
+
WHERE
|
157
|
+
#{@schema_filter.call :routine_schema}
|
158
|
+
AND external_name IS NULL
|
159
|
+
ORDER BY
|
160
|
+
1, 2
|
161
|
+
SQL
|
162
|
+
_recordset.each_with_object(@cache){ |row, h|
|
163
|
+
h[:schemas][row["routine_schema"]][:functions][row["routine_name"]] = {
|
164
|
+
external_language: row["external_language"],
|
165
|
+
comment: row["comment"],
|
166
|
+
arguments: row["arguments"].split(",").map{ |arg| parse_function_argument arg }
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
# Load foreign keys
|
171
|
+
_recordset = @conn.exec <<~SQL
|
172
|
+
SELECT
|
173
|
+
tc.table_schema,
|
174
|
+
tc.table_name,
|
175
|
+
tc.constraint_name,
|
176
|
+
tc.constraint_type,
|
177
|
+
kcu.column_name,
|
178
|
+
tc.is_deferrable,
|
179
|
+
tc.initially_deferred,
|
180
|
+
rc.match_option AS match_type,
|
181
|
+
|
182
|
+
rc.update_rule AS on_update,
|
183
|
+
rc.delete_rule AS on_delete,
|
184
|
+
ccu.table_schema AS references_schema,
|
185
|
+
ccu.table_name AS references_table,
|
186
|
+
ccu.column_name AS references_field
|
187
|
+
|
188
|
+
FROM
|
189
|
+
information_schema.table_constraints tc
|
190
|
+
|
191
|
+
LEFT JOIN information_schema.key_column_usage kcu
|
192
|
+
ON tc.constraint_catalog = kcu.constraint_catalog
|
193
|
+
AND tc.constraint_schema = kcu.constraint_schema
|
194
|
+
AND tc.constraint_name = kcu.constraint_name
|
195
|
+
|
196
|
+
LEFT JOIN information_schema.referential_constraints rc
|
197
|
+
ON tc.constraint_catalog = rc.constraint_catalog
|
198
|
+
AND tc.constraint_schema = rc.constraint_schema
|
199
|
+
AND tc.constraint_name = rc.constraint_name
|
200
|
+
|
201
|
+
LEFT JOIN information_schema.constraint_column_usage ccu
|
202
|
+
ON rc.unique_constraint_catalog = ccu.constraint_catalog
|
203
|
+
AND rc.unique_constraint_schema = ccu.constraint_schema
|
204
|
+
AND rc.unique_constraint_name = ccu.constraint_name
|
205
|
+
|
206
|
+
WHERE
|
207
|
+
tc.constraint_type = 'FOREIGN KEY'
|
208
|
+
ORDER BY
|
209
|
+
1, 2
|
210
|
+
SQL
|
211
|
+
_recordset.each_with_object(@cache){ |row, h|
|
212
|
+
h[:schemas][row["table_schema"]][:tables][row["table_name"]][:foreign_keys][row["column_name"]] = row
|
213
|
+
}
|
214
|
+
|
215
|
+
# Load indexes
|
216
|
+
_recordset = @conn.exec <<~SQL
|
217
|
+
SELECT
|
218
|
+
ns.nspname AS table_schema,
|
219
|
+
t.relname AS table_name,
|
220
|
+
U.usename AS user_name,
|
221
|
+
i.relname AS index_name,
|
222
|
+
idx.indisunique AS is_unique,
|
223
|
+
idx.indisprimary AS is_primary,
|
224
|
+
am.amname AS index_type,
|
225
|
+
idx.indkey,
|
226
|
+
ARRAY(
|
227
|
+
SELECT pg_get_indexdef(idx.indexrelid, k + 1, TRUE)
|
228
|
+
FROM generate_subscripts(idx.indkey, 1) AS k
|
229
|
+
ORDER BY k
|
230
|
+
) AS index_keys,
|
231
|
+
(idx.indexprs IS NOT NULL) OR (idx.indkey::int[] @> array[0]) AS is_functional,
|
232
|
+
idx.indpred IS NOT NULL AS is_partial
|
233
|
+
FROM
|
234
|
+
pg_index AS idx
|
235
|
+
JOIN pg_class AS i ON i.oid = idx.indexrelid
|
236
|
+
JOIN pg_class AS t ON t.oid = idx.indrelid
|
237
|
+
JOIN pg_am AS am ON i.relam = am.oid
|
238
|
+
JOIN pg_namespace AS ns ON i.relnamespace = ns.oid
|
239
|
+
JOIN pg_user AS U ON i.relowner = U.usesysid
|
240
|
+
WHERE
|
241
|
+
#{@schema_filter.call :"ns.nspname"}
|
242
|
+
ORDER BY
|
243
|
+
1, 2, idx.indisprimary DESC, i.relname
|
244
|
+
SQL
|
245
|
+
_recordset.each_with_object(@cache){ |row, h|
|
246
|
+
h[:schemas][row["table_schema"]][:tables][row["table_name"]][:indexes][row["index_name"]] = row
|
247
|
+
}
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
def parse_function_argument arg
|
252
|
+
|
253
|
+
# Determine the argument's mode
|
254
|
+
arg = arg.strip
|
255
|
+
argmode = case arg
|
256
|
+
when /^VARIADIC\b/, /^OUT\b/, /^INOUT\b/, /^IN\b/
|
257
|
+
_parts = arg.split(" ")
|
258
|
+
_mode = _parts.shift
|
259
|
+
arg = _parts.join(" ")
|
260
|
+
_mode
|
261
|
+
else
|
262
|
+
"IN"
|
263
|
+
end
|
264
|
+
|
265
|
+
# Determine it's name and type
|
266
|
+
if arg.count(" ") > 0
|
267
|
+
name, *type = arg.split " "
|
268
|
+
type = type.join " "
|
269
|
+
else
|
270
|
+
name = nil
|
271
|
+
type = arg
|
272
|
+
end
|
273
|
+
|
274
|
+
{name: name, type: type, mode: argmode}
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
end
|
data/lib/pg/doc/version.rb
CHANGED
data/pg-doc.gemspec
CHANGED
@@ -2,22 +2,32 @@ lib = File.expand_path("../lib", __FILE__)
|
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
3
|
require "pg/doc/version"
|
4
4
|
|
5
|
-
Gem::Specification.new do |
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "pg-doc"
|
7
|
+
gem.version = PG::Doc::VERSION
|
8
|
+
gem.authors = ["Kenaniah Cerny"]
|
9
|
+
gem.email = ["kenaniah@gmail.com"]
|
10
|
+
gem.license = "MIT"
|
11
|
+
gem.required_ruby_version = '~> 2.3'
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
gem.summary = "Automatic documentation for your PostgreSQL database"
|
14
|
+
gem.homepage = "https://github.com/kenaniah/pg-doc"
|
13
15
|
|
14
|
-
|
16
|
+
gem.files = `git ls-files -z`.split("\x0").reject do |f|
|
15
17
|
f.match(%r{^(test|spec|features)/})
|
16
18
|
end
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
gem.bindir = "exe"
|
20
|
+
gem.executables = gem.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
gem.require_paths = ["lib"]
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
+
gem.add_development_dependency "bundler", "~> 1.16"
|
24
|
+
gem.add_development_dependency "rake", "~> 10.0"
|
25
|
+
gem.add_development_dependency "appraisal"
|
26
|
+
gem.add_development_dependency "minitest"
|
27
|
+
gem.add_development_dependency "pry"
|
28
|
+
|
29
|
+
gem.add_dependency "pg", "~> 1.0"
|
30
|
+
gem.add_dependency "sinatra", "~> 2.0"
|
31
|
+
gem.add_dependency "redcarpet", "~> 3.4"
|
32
|
+
gem.add_dependency "mime-types", "~> 3.0"
|
23
33
|
end
|
data/static/pg-doc.js
ADDED
data/static/style.css
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
BODY {
|
2
|
+
background-color: #FFFFFF;
|
3
|
+
}
|
4
|
+
CODE {
|
5
|
+
font-size: 90%;
|
6
|
+
color: rgb(199, 37, 78);
|
7
|
+
background-color: rgb(249, 242, 244);
|
8
|
+
padding: 2px 4px;
|
9
|
+
border-radius: 4px;
|
10
|
+
}
|
11
|
+
CODE:empty {
|
12
|
+
display: none;
|
13
|
+
}
|
14
|
+
.ui.text.main.container {
|
15
|
+
max-width: calc(100% - 14em) !important;
|
16
|
+
width: auto !important;
|
17
|
+
margin-top: 7em;
|
18
|
+
}
|
19
|
+
.scroll-padding {
|
20
|
+
padding-bottom: 20vh;
|
21
|
+
}
|
22
|
+
.menu .subheader {
|
23
|
+
color: white;
|
24
|
+
margin: 1em 1em 0.2em 1em;
|
25
|
+
padding-bottom: 0.3em;
|
26
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
27
|
+
}
|
28
|
+
.ui.visible.left.sidebar~.pusher {
|
29
|
+
width: calc(100% - 260px);
|
30
|
+
}
|
31
|
+
.ui.main H1,
|
32
|
+
.ui.main H2,
|
33
|
+
.ui.main H3,
|
34
|
+
.ui.main H4,
|
35
|
+
.ui.main H5,
|
36
|
+
.ui.main H6 {
|
37
|
+
margin-top: 32px;
|
38
|
+
margin-bottom: 16px;
|
39
|
+
padding-bottom: 2px;
|
40
|
+
border-bottom: 1px solid rgb(234, 236, 239);
|
41
|
+
}
|
42
|
+
.ui.table TD {
|
43
|
+
overflow: scroll !important;
|
44
|
+
text-overflow: unset !important;
|
45
|
+
white-space: nowrap;
|
46
|
+
}
|
47
|
+
|
48
|
+
.ui.table TD::-webkit-scrollbar {
|
49
|
+
width: 0;
|
50
|
+
height: 0;
|
51
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<div class="ui left vertical inverted visible sidebar menu">
|
2
|
+
|
3
|
+
<a class="item" href="<%= url '/' %>">
|
4
|
+
<span><i class="home icon"></i> Home</span>
|
5
|
+
</a>
|
6
|
+
|
7
|
+
<% @cache[:schemas].each do |schema, data| %>
|
8
|
+
|
9
|
+
<div class="js-submenu link item">
|
10
|
+
<span><i class="database icon"></i> <%= schema %></span>
|
11
|
+
<i class="caret right icon"></i>
|
12
|
+
<i class="caret down icon transition hidden"></i>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="menu transition hidden">
|
16
|
+
|
17
|
+
<div class="subheader">Schema</div>
|
18
|
+
<a class="item" href="<%= url "/schemas/#{schema}" %>"><span><i class="database icon"></i> <%= schema %></span></a>
|
19
|
+
|
20
|
+
<% unless data[:tables].empty? %>
|
21
|
+
<div class="subheader">Tables</div>
|
22
|
+
<% data[:tables].each do |table_name, table| %>
|
23
|
+
<a class="item" href="<%= url "/schemas/#{schema}/tables/#{table_name}" %>"><span><i class="table icon"></i> <%= table_name %></span></a>
|
24
|
+
<% end %>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<% unless data[:views].empty? %>
|
28
|
+
<div class="subheader">Views</div>
|
29
|
+
<% data[:views].each do |view_name, view| %>
|
30
|
+
<a class="item" href="<%= url "/schemas/#{schema}/views/#{view_name}" %>"><span><i class="table icon"></i> <%= view_name %></span></a>
|
31
|
+
<% end %>
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<% unless data[:functions].empty? %>
|
35
|
+
<div class="subheader">Functions</div>
|
36
|
+
<% data[:functions].each do |routine_name, routine| %>
|
37
|
+
<a class="item" href="<%= url "/schemas/#{schema}/functions/#{routine_name}" %>"><span><i class="table icon"></i> <%= routine_name %></span></a>
|
38
|
+
<% end %>
|
39
|
+
<% end %>
|
40
|
+
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<% end %>
|
44
|
+
|
45
|
+
<div class="scroll-padding"></div>
|
46
|
+
|
47
|
+
</div>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<%
|
2
|
+
if File.exists? file
|
3
|
+
markdown = Redcarpet::Markdown.new Redcarpet::Render::HTML, tables: true, no_intra_emphasis: true
|
4
|
+
contents = markdown.render File.read(file)
|
5
|
+
contents = contents.gsub("<table>", "<table class='ui celled table'>")
|
6
|
+
contents = contents.gsub("<img ", "<img class='ui fluid image'")
|
7
|
+
contents = contents.gsub(/<img[^>]+src=(['"])([^'"]+)\1[^>]*>/i) { |match|
|
8
|
+
|
9
|
+
next $2 if $2.include? "//"
|
10
|
+
|
11
|
+
# Attempt to find the refrenced file
|
12
|
+
img = File.expand_path($2, File.dirname(file))
|
13
|
+
next $2 unless File.exists? img
|
14
|
+
|
15
|
+
# Only allow images through
|
16
|
+
mime_type = MIME::Types.type_for(File.extname img).first.to_s
|
17
|
+
next $2 unless mime_type.start_with? "image/"
|
18
|
+
|
19
|
+
# Base64 encode & inline the image
|
20
|
+
match.sub $2, "data:#{mime_type};base64,#{Base64.encode64(File.read img)}"
|
21
|
+
|
22
|
+
}
|
23
|
+
%>
|
24
|
+
<div class="rendered-markdown">
|
25
|
+
<%= contents %>
|
26
|
+
</div>
|
27
|
+
<% else %>
|
28
|
+
<div class="ui warning message">
|
29
|
+
<div class="header">No markdown file found</div>
|
30
|
+
The file <code><%= file %></code> does not exist.
|
31
|
+
</div>
|
32
|
+
<% end %>
|
data/views/index.erb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
<h1 class="ui header">
|
2
|
+
Database Documentation
|
3
|
+
</h1>
|
4
|
+
|
5
|
+
<p>
|
6
|
+
Welcome to <code>PG::Doc</code>!
|
7
|
+
</p>
|
8
|
+
|
9
|
+
<p>
|
10
|
+
This tool automatically generates documentation for PostgreSQL databases.
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<p>
|
14
|
+
You can learn more about this tool at
|
15
|
+
<a href="https://github.com/kenaniah/pg-doc" target="_blank">https://github.com/kenaniah/pg-doc</a>.
|
16
|
+
</p>
|
data/views/layout.erb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<!-- Standard Meta -->
|
5
|
+
<meta charset="utf-8" />
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
8
|
+
|
9
|
+
<!-- Site Properties -->
|
10
|
+
<title>Database Documentation</title>
|
11
|
+
|
12
|
+
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.13/semantic.min.css">
|
13
|
+
<link rel="stylesheet" type="text/css" href="<%= url '/style.css' %>">
|
14
|
+
|
15
|
+
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
|
16
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.13/semantic.min.js"></script>
|
17
|
+
<script src="<%= url '/pg-doc.js' %>"></script>
|
18
|
+
|
19
|
+
</head>
|
20
|
+
<body class="pushable">
|
21
|
+
|
22
|
+
<%= erb :'includes/left_menu' %>
|
23
|
+
|
24
|
+
<div class="pusher">
|
25
|
+
<div class="ui main container text scroll-padding">
|
26
|
+
<%= yield %>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
</body>
|
31
|
+
|
32
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= object %>
|
@@ -0,0 +1,94 @@
|
|
1
|
+
<%
|
2
|
+
def fk_action_color action
|
3
|
+
case action
|
4
|
+
when 'CASCADE'
|
5
|
+
'negative'
|
6
|
+
when 'SET NULL'
|
7
|
+
'warning'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
%>
|
11
|
+
|
12
|
+
<h1>
|
13
|
+
Table - <code><%= params["schema"] %>.<%= params["name"] %></code>
|
14
|
+
</h1>
|
15
|
+
|
16
|
+
<% if object[:comment] %>
|
17
|
+
<h2>Database Comment</h2>
|
18
|
+
<p><%= object[:comment] %></p>
|
19
|
+
<% end %>
|
20
|
+
|
21
|
+
<%= render_markdown "schema/#{params["schema"]}/#{params["name"]}.md" %>
|
22
|
+
|
23
|
+
<h2>Columns</h2>
|
24
|
+
|
25
|
+
<table class="ui compact fixed celled table">
|
26
|
+
<thead>
|
27
|
+
<th>Column Name</th>
|
28
|
+
<th>Data Type</th>
|
29
|
+
<th class="three wide">Required Field?</th>
|
30
|
+
<th>Default Value</th>
|
31
|
+
</thead>
|
32
|
+
<tbody>
|
33
|
+
<% object[:columns].each do |row| %>
|
34
|
+
<tr>
|
35
|
+
<td>
|
36
|
+
<% if fk = object[:foreign_keys].fetch(row["column_name"], nil) %>
|
37
|
+
<i class="icon book"></i>
|
38
|
+
<a href="<%= url "/schemas/#{fk["references_schema"]}/tables/#{fk["references_table"]}" %>" title="<%= fk["references_schema"] %>.<%= fk["references_table"] %>"><%= row["column_name"] %></a>
|
39
|
+
<% else %>
|
40
|
+
<%= row["column_name"] %>
|
41
|
+
<% end %>
|
42
|
+
</td>
|
43
|
+
<td><%= row["data_type"] %></td>
|
44
|
+
<td class="<%= row["is_nullable"] == 'NO' && row["column_default"].to_s.empty? ? 'warning' : '' %>"><%= row["is_nullable"] == 'NO' ? '<i class="icon check"></i>' : ''%></td>
|
45
|
+
<td><code><%= row["column_default"] %></code></td>
|
46
|
+
</tr>
|
47
|
+
<% end %>
|
48
|
+
</tbody>
|
49
|
+
</table>
|
50
|
+
|
51
|
+
|
52
|
+
<% unless object[:foreign_keys].empty? %>
|
53
|
+
<h2>Foreign Keys</h2>
|
54
|
+
<table class="ui compact fixed celled table">
|
55
|
+
<thead>
|
56
|
+
<th>Column Name</th>
|
57
|
+
<th>References</th>
|
58
|
+
<th class="three wide">On Delete</th>
|
59
|
+
<th>Constraint Name</th>
|
60
|
+
</thead>
|
61
|
+
<tbody>
|
62
|
+
<% object[:foreign_keys].each do |column_name, row| %>
|
63
|
+
<tr>
|
64
|
+
<td><%= row["column_name"] %></td>
|
65
|
+
<td><i class="icon book"></i> <a href="<%= url "/schemas/#{row["references_schema"]}/tables/#{row["references_table"]}" %>" title="<%= row["references_schema"] %>.<%= row["references_table"] %>"><%= "#{row["references_schema"]}.#{row["references_table"]}" %></a></td>
|
66
|
+
<td class="<%= fk_action_color row["on_delete"] %>"><%= row["on_delete"] %></td>
|
67
|
+
<td><%= row["constraint_name"] %></td>
|
68
|
+
</tr>
|
69
|
+
<% end %>
|
70
|
+
</tbody>
|
71
|
+
</table>
|
72
|
+
<% end %>
|
73
|
+
|
74
|
+
<h2>Indexes</h2>
|
75
|
+
<table class="ui compact fixed celled table">
|
76
|
+
<thead>
|
77
|
+
<th>Index Name</th>
|
78
|
+
<th class="two wide">Primary?</th>
|
79
|
+
<th class="two wide">Unique?</th>
|
80
|
+
<th class="two wide">Type</th>
|
81
|
+
<th>Columns</th>
|
82
|
+
</thead>
|
83
|
+
<tbody>
|
84
|
+
<% object[:indexes].each do |index_name, row| %>
|
85
|
+
<tr>
|
86
|
+
<td><%= row["index_name"] %></td>
|
87
|
+
<td><% if row["is_primary"] == "t" %><i class="icon check"></i><% end %></td>
|
88
|
+
<td><% if row["is_unique"] == "t" %><i class="icon check"></i><% end %></td>
|
89
|
+
<td><%= row["index_type"] %></td>
|
90
|
+
<td><%= row["index_keys"][1..-2].split(",").join ", " %></td>
|
91
|
+
</tr>
|
92
|
+
<% end %>
|
93
|
+
</tbody>
|
94
|
+
</table>
|
@@ -0,0 +1 @@
|
|
1
|
+
<h2>Coming Soon!</h2>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg-doc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenaniah Cerny
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -38,6 +38,104 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: appraisal
|
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
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
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'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pg
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sinatra
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '2.0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: redcarpet
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.4'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.4'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: mime-types
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '3.0'
|
41
139
|
description:
|
42
140
|
email:
|
43
141
|
- kenaniah@gmail.com
|
@@ -46,16 +144,35 @@ extensions: []
|
|
46
144
|
extra_rdoc_files: []
|
47
145
|
files:
|
48
146
|
- ".gitignore"
|
147
|
+
- ".travis.yml"
|
148
|
+
- Appraisals
|
49
149
|
- Gemfile
|
50
150
|
- README.md
|
51
151
|
- Rakefile
|
52
152
|
- bin/console
|
153
|
+
- bin/rackup
|
154
|
+
- bin/rake
|
53
155
|
- bin/setup
|
156
|
+
- config.ru
|
157
|
+
- gemfiles/.bundle/config
|
158
|
+
- gemfiles/pg_1.x.gemfile
|
54
159
|
- lib/pg/doc.rb
|
160
|
+
- lib/pg/doc/engine.rb
|
55
161
|
- lib/pg/doc/version.rb
|
56
162
|
- pg-doc.gemspec
|
163
|
+
- static/pg-doc.js
|
164
|
+
- static/style.css
|
165
|
+
- views/includes/left_menu.erb
|
166
|
+
- views/includes/markdown.erb
|
167
|
+
- views/index.erb
|
168
|
+
- views/layout.erb
|
169
|
+
- views/objects/function.erb
|
170
|
+
- views/objects/schema.erb
|
171
|
+
- views/objects/table.erb
|
172
|
+
- views/objects/view.erb
|
57
173
|
homepage: https://github.com/kenaniah/pg-doc
|
58
|
-
licenses:
|
174
|
+
licenses:
|
175
|
+
- MIT
|
59
176
|
metadata: {}
|
60
177
|
post_install_message:
|
61
178
|
rdoc_options: []
|
@@ -63,9 +180,9 @@ require_paths:
|
|
63
180
|
- lib
|
64
181
|
required_ruby_version: !ruby/object:Gem::Requirement
|
65
182
|
requirements:
|
66
|
-
- - "
|
183
|
+
- - "~>"
|
67
184
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
185
|
+
version: '2.3'
|
69
186
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
187
|
requirements:
|
71
188
|
- - ">="
|