pg-doc 0.0.0 → 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.
- 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
|
+
[](http://badge.fury.io/rb/pg-doc)
|
|
4
|
+
[](http://travis-ci.org/kenaniah/pg-doc)
|
|
5
|
+
[](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
|
- - ">="
|