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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bd9bd7f1381b0643750a98867020e11f3426633c
4
- data.tar.gz: f538110c20fa04a792c2a537e1c00c3e9d716ea8
3
+ metadata.gz: 50222b07884abd889b86cc5cc645916e0c2f992a
4
+ data.tar.gz: b49449f6ff9e3ac829231db3e4c5290e35f209c0
5
5
  SHA512:
6
- metadata.gz: 41212625fb051f99a0739257a7eeda564e5d55ebad0a6ec48e7a9bd3ce3cd6523a5ada50b55c726852edd7230d267ddcf4e9f086fdd258c4ba23154a95e661d0
7
- data.tar.gz: 3e84f0ccd6e9f09f2490ac48c2fbe07ee217c85cf9439ba406cd4833aaf01179f1a0d29959e205f27ab52c9fe14a32c19a5e5cbabee88e930ce89e7e5058b1d4
6
+ metadata.gz: aa70ad7c57bcff40d6bd6d0080760e354bdf13e3a9171ba7b69ef2278c3719b3f21f89cf7d4dd4d24ecf396c6fd6f78327d3f65f97be5605950b3484ce21df45
7
+ data.tar.gz: 6775c5074e41bf81874bbf483bbab6bcc3e7f111485f79f26d0bfba45e56ae20170b4b69fd550ece99c88ea405be02a08fc1ca731ccb51590ff53f334d393ebc
data/.gitignore CHANGED
@@ -5,5 +5,8 @@
5
5
  /doc/
6
6
  /pkg/
7
7
  /spec/reports/
8
+ /test/reports/
8
9
  /tmp/
9
10
  *.gem
11
+ Gemfile.lock
12
+ gemfiles/*.lock
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3
4
+ - 2.4
5
+ - 2.5
6
+ gemfile:
7
+ - gemfiles/pg_1.x.gemfile
8
+ before_install:
9
+ - gem update --system
10
+ - gem update bundler
11
+ services: postgresql
@@ -0,0 +1,3 @@
1
+ appraise "pg-1.x" do
2
+ gem "pg", "~> 1.0"
3
+ end
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # PG::Doc
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/pg/doc`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Delete this and the text above, and describe your gem
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
- ## Usage
25
+ ## To Do
24
26
 
25
- TODO: Write usage instructions here
26
-
27
- ## Development
28
-
29
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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
@@ -1,2 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
- task :default => :spec
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.test_files = FileList['test/**/*_test.rb']
6
+ t.libs << "test"
7
+ end
8
+
9
+ task :default => :test
@@ -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")
@@ -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")
@@ -0,0 +1,5 @@
1
+ require "bundler"
2
+ Bundler.require
3
+
4
+ require "pg/doc"
5
+ run PG::Doc.new ENV['DATABASE_URL'], path_to_markdowns: ENV['PATH_TO_MARKDOWNS']
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_RETRY: "1"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pg", "~> 1.0"
6
+
7
+ gemspec path: "../"
@@ -1,7 +1,12 @@
1
+ require "pg"
1
2
  require "pg/doc/version"
3
+ require "pg/doc/engine"
4
+
5
+ require "base64"
6
+ require "mime-types"
7
+ require "redcarpet"
2
8
 
3
9
  module PG
4
10
  module Doc
5
- # Your code goes here...
6
11
  end
7
12
  end
@@ -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
@@ -1,5 +1,5 @@
1
1
  module PG
2
2
  module Doc
3
- VERSION = "0.0.0"
3
+ VERSION = "0.0.1"
4
4
  end
5
5
  end
@@ -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 |spec|
6
- spec.name = "pg-doc"
7
- spec.version = PG::Doc::VERSION
8
- spec.authors = ["Kenaniah Cerny"]
9
- spec.email = ["kenaniah@gmail.com"]
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
- spec.summary = "Automatic documentation for your PostgreSQL database"
12
- spec.homepage = "https://github.com/kenaniah/pg-doc"
13
+ gem.summary = "Automatic documentation for your PostgreSQL database"
14
+ gem.homepage = "https://github.com/kenaniah/pg-doc"
13
15
 
14
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ gem.files = `git ls-files -z`.split("\x0").reject do |f|
15
17
  f.match(%r{^(test|spec|features)/})
16
18
  end
17
- spec.bindir = "exe"
18
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
- spec.require_paths = ["lib"]
19
+ gem.bindir = "exe"
20
+ gem.executables = gem.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ gem.require_paths = ["lib"]
20
22
 
21
- spec.add_development_dependency "bundler", "~> 1.16"
22
- spec.add_development_dependency "rake", "~> 10.0"
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
@@ -0,0 +1,9 @@
1
+ $(document).ready(() =>
2
+
3
+ // Toggles submenues in navigation
4
+ $(document).on("click", ".js-submenu", (event) => {
5
+ $(event.currentTarget).find("I.caret").transition('toggle')
6
+ $(event.currentTarget).next("DIV.menu").transition('toggle')
7
+ })
8
+
9
+ );
@@ -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 %>
@@ -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>
@@ -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,5 @@
1
+ <h1>
2
+ Schema - <code><%= params["schema"] %></code>
3
+ </h1>
4
+
5
+ <%= render_markdown "schema/#{params["schema"]}/README.md" %>
@@ -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.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-12 00:00:00.000000000 Z
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: '0'
185
+ version: '2.3'
69
186
  required_rubygems_version: !ruby/object:Gem::Requirement
70
187
  requirements:
71
188
  - - ">="