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 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
  - - ">="