database_documenter 0.1.6
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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +45 -0
- data/README.md +61 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/database_documenter.gemspec +29 -0
- data/lib/database_documenter.rb +29 -0
- data/lib/database_documenter/column_description.rb +105 -0
- data/lib/database_documenter/configuration.rb +12 -0
- data/lib/database_documenter/database_comment.rb +15 -0
- data/lib/database_documenter/database_comment/base_database_comment.rb +16 -0
- data/lib/database_documenter/database_comment/mysql_database_comment.rb +28 -0
- data/lib/database_documenter/database_comment/postgres_database_comment.rb +39 -0
- data/lib/database_documenter/railtie.rb +6 -0
- data/lib/database_documenter/tables_sql.rb +55 -0
- data/lib/database_documenter/version.rb +3 -0
- data/lib/tasks/generate_db_document.rake +96 -0
- data/lib/tasks/generate_dd_initializer.rake +9 -0
- data/lib/tasks/templates/database_documenter.rb +5 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 79f3c7ce476383f98904cc065f592a2bfc522507b0bb5e9777b98429f206fde1
|
4
|
+
data.tar.gz: 52c6cebb47928cb00fc4a1e7f94aa6b807709970e78b3cb38529a7eff34e896c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 70679fc84a3e1e0b9e143c0f642d78ed6541cb899e07d7329db83399e5e93788eab57e6dda6b32a8e5236132ff31caeed17545a9c2554ff5f9b200ab85adacf8
|
7
|
+
data.tar.gz: 3fd5a31dd095a61c12c40c3c701bb683f47be404e18d6018308d3dbc956b3bff65caafd68193479ecd73bf5371d32487de2d2f4ef46f6df09ae739f1830b45de
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
database_documenter (0.1.5)
|
5
|
+
caracal (= 1.4.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
caracal (1.4.1)
|
11
|
+
nokogiri (~> 1.6)
|
12
|
+
rubyzip (~> 1.1)
|
13
|
+
tilt (>= 1.4)
|
14
|
+
diff-lcs (1.3)
|
15
|
+
mini_portile2 (2.4.0)
|
16
|
+
nokogiri (1.10.3)
|
17
|
+
mini_portile2 (~> 2.4.0)
|
18
|
+
rake (10.5.0)
|
19
|
+
rspec (3.8.0)
|
20
|
+
rspec-core (~> 3.8.0)
|
21
|
+
rspec-expectations (~> 3.8.0)
|
22
|
+
rspec-mocks (~> 3.8.0)
|
23
|
+
rspec-core (3.8.2)
|
24
|
+
rspec-support (~> 3.8.0)
|
25
|
+
rspec-expectations (3.8.4)
|
26
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
+
rspec-support (~> 3.8.0)
|
28
|
+
rspec-mocks (3.8.1)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.8.0)
|
31
|
+
rspec-support (3.8.2)
|
32
|
+
rubyzip (1.2.3)
|
33
|
+
tilt (2.0.9)
|
34
|
+
|
35
|
+
PLATFORMS
|
36
|
+
ruby
|
37
|
+
|
38
|
+
DEPENDENCIES
|
39
|
+
bundler (~> 1.17)
|
40
|
+
database_documenter!
|
41
|
+
rake (~> 10.0)
|
42
|
+
rspec (~> 3.0)
|
43
|
+
|
44
|
+
BUNDLED WITH
|
45
|
+
1.17.3
|
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# DatabaseDocumenter
|
2
|
+
|
3
|
+
Welcome to Database Documenter gem! We created this gem to generate database documentation for rails applications.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
1. Generate database documentation as a Word document.
|
8
|
+
2. Generate Description for columns based on it is type and name, Also handle Enums and STI and AASM.
|
9
|
+
3. Easy to change the generated description by adding a comment on your database.
|
10
|
+
4. Hide sample values of desired columns using configuration.
|
11
|
+
5. You can Ignore models inside certain namespaces.
|
12
|
+
6. Works on MySQL and PostgreSQL database.
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
gem 'database_documenter'
|
18
|
+
```
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle install
|
23
|
+
|
24
|
+
## Configuration
|
25
|
+
|
26
|
+
To generate the gem configuration file run this rake task in the application directory
|
27
|
+
|
28
|
+
$ bundle exec rake generate_dd_initializer
|
29
|
+
|
30
|
+
or create the configuration file manually in this path `config/initializers/database_documenter.rb` :
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
DatabaseDocumenter.configure do |config|
|
34
|
+
config.skipped_modules = %w(NAMESPACE)
|
35
|
+
config.hidden_values_columns = %w(col1 col2)
|
36
|
+
config.footer = "Generated by Company" # Footer beside the pagination
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
in the application folder run this rake task and then you will found word document named `database.docx` in your application folder:
|
43
|
+
|
44
|
+
$ bundle exec rake generate_db_document
|
45
|
+
|
46
|
+
## Override generated description
|
47
|
+
You can override it by adding comment to your schema using one of the following options:
|
48
|
+
|
49
|
+
### Rails 4
|
50
|
+
use [migration comments](https://github.com/pinnymz/migration_comments) gem or [pg_comment](https://github.com/albertosaurus/pg_comment)
|
51
|
+
|
52
|
+
### Rails 5.2
|
53
|
+
use `change_column_comment` and `change_table_comment` methods in rails 5
|
54
|
+
|
55
|
+
## TODO
|
56
|
+
|
57
|
+
- Add more Configurations.
|
58
|
+
- Add OverCommit
|
59
|
+
- Generate the ERD with the file.
|
60
|
+
- Clean code.
|
61
|
+
- Add test cases.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "database_documenter"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "database_documenter/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "database_documenter"
|
8
|
+
spec.version = DatabaseDocumenter::VERSION
|
9
|
+
spec.authors = ["Ahmed Yehia"]
|
10
|
+
spec.email = ["ruby@espace.com.eg"]
|
11
|
+
spec.homepage = "https://github.com/espace/db_documenter"
|
12
|
+
|
13
|
+
spec.summary = %q{Generate Database Documentation as a word document}
|
14
|
+
spec.description = %q{Generate Database Documentation as a word document}
|
15
|
+
|
16
|
+
# Specify which files should be added to the gem when it is released.
|
17
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
|
+
end
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
28
|
+
spec.add_dependency 'caracal', '=1.4.1'
|
29
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'database_documenter/configuration'
|
2
|
+
require "database_documenter/version"
|
3
|
+
require 'database_documenter/database_comment'
|
4
|
+
require 'database_documenter/database_comment/base_database_comment'
|
5
|
+
require 'database_documenter/database_comment/mysql_database_comment'
|
6
|
+
require 'database_documenter/database_comment/postgres_database_comment'
|
7
|
+
require 'database_documenter/tables_sql'
|
8
|
+
require 'database_documenter/column_description'
|
9
|
+
require 'caracal'
|
10
|
+
require "database_documenter/railtie" if defined?(Rails)
|
11
|
+
|
12
|
+
module DatabaseDocumenter
|
13
|
+
class Error < StandardError; end
|
14
|
+
class << self
|
15
|
+
attr_accessor :configuration
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.configuration
|
19
|
+
@configuration ||= Configuration.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.reset
|
23
|
+
@configuration = Configuration.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.configure
|
27
|
+
yield(configuration)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module DatabaseDocumenter::ColumnDescription
|
3
|
+
|
4
|
+
def self.generate(column_name, column_type, klass)
|
5
|
+
klass_name = klass.name.demodulize.titleize
|
6
|
+
|
7
|
+
# handle enums
|
8
|
+
if klass.defined_enums.keys.include?(column_name)
|
9
|
+
return generate_enum_column_description(column_name, column_type, klass_name, klass)
|
10
|
+
end
|
11
|
+
|
12
|
+
# handle assm
|
13
|
+
if klass.respond_to?(:aasm) && klass.aasm.attribute_name.to_s == column_name
|
14
|
+
return generate_assm_column_description(column_name, column_type, klass_name, klass)
|
15
|
+
elsif klass.subclasses.select { |x| x.respond_to?(:aasm) }[0] != nil
|
16
|
+
subklass = klass.subclasses.select { |x| x.respond_to?(:aasm) }[0]
|
17
|
+
if subklass.aasm.attribute_name.to_s == column_name
|
18
|
+
return generate_assm_column_description(column_name, column_type, klass_name, subklass)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Default
|
23
|
+
if self.respond_to?("generate_#{column_type}_column_description")
|
24
|
+
send("generate_#{column_type}_column_description", column_name, column_type, klass_name)
|
25
|
+
else
|
26
|
+
self.generate_with_default_rules(column_name, column_type, klass_name, klass)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.generate_datetime_column_description(column_name, column_type, klass_name)
|
31
|
+
case column_name
|
32
|
+
when 'remember_created_at'
|
33
|
+
"Date when remember me created"
|
34
|
+
when 'reset_password_sent_at'
|
35
|
+
"Date when reset password sent"
|
36
|
+
when 'current_sign_in_at'
|
37
|
+
"Date when the user current signed in"
|
38
|
+
when "last_sign_in_at"
|
39
|
+
"Date when the user last signed in"
|
40
|
+
when 'remember_created_at'
|
41
|
+
"Date when remember token created"
|
42
|
+
when /.*_at/
|
43
|
+
"Date when the row was #{column_name.titlecase.downcase[0..-4]}"
|
44
|
+
else
|
45
|
+
"Date when the row was #{column_name.titlecase.downcase}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.generate_boolean_column_description(column_name, column_type, klass_name)
|
50
|
+
case column_name
|
51
|
+
when /.*active/, /has_.*/, "canceled"
|
52
|
+
"Is #{klass_name.titlecase.downcase} #{column_name.titlecase.downcase}"
|
53
|
+
when /is_.*/
|
54
|
+
column_name.titlecase.downcase
|
55
|
+
else
|
56
|
+
"Is #{klass_name.titlecase.downcase} has #{column_name.titlecase.downcase}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.generate_with_default_rules(column_name, column_type, klass_name, klass)
|
61
|
+
case column_name
|
62
|
+
when 'type'
|
63
|
+
values_hash = Hash[klass.subclasses.collect { |k| [k.name.underscore.humanize, k.name] } ]
|
64
|
+
represent_multi_value_column(column_name, klass_name, values_hash)
|
65
|
+
when /.*_code/
|
66
|
+
refered_table_name = column_name.scan(/(.*)_code/)[0][0]
|
67
|
+
"Code of #{refered_table_name.titleize}".capitalize
|
68
|
+
when /.*_id/
|
69
|
+
refered_table_name = column_name.scan(/(.*)_id/)[0][0]
|
70
|
+
"Id of #{refered_table_name.titleize}".capitalize
|
71
|
+
when /.*_type/
|
72
|
+
refered_table_name = column_name.scan(/(.*)_type/)[0][0]
|
73
|
+
"Type of #{refered_table_name.titleize}".capitalize
|
74
|
+
when /.*_count/
|
75
|
+
refered_table_name = column_name.scan(/(.*)_count/)[0][0]
|
76
|
+
"Count of #{refered_table_name.titleize}".capitalize
|
77
|
+
else
|
78
|
+
"#{column_name.titlecase.downcase} of #{klass_name.titleize}".capitalize
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.generate_enum_column_description(column_name, column_type, klass_name, klass)
|
83
|
+
values_hash = klass.defined_enums[column_name]
|
84
|
+
represent_multi_value_column(column_name, klass_name, values_hash)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.generate_assm_column_description(column_name, column_type, klass_name, klass)
|
88
|
+
values_hash = Hash[klass.aasm.states.collect { |k| [k.name.to_s.humanize, k.name.to_s] } ]
|
89
|
+
represent_multi_value_column(column_name, klass_name, values_hash)
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.represent_multi_value_column(column_name, klass_name, values_hash)
|
93
|
+
broken_cell_para = Caracal::Core::Models::TableCellModel.new do |c|
|
94
|
+
c.p "#{column_name.titlecase.downcase} of #{klass_name.titleize}, possible values:"
|
95
|
+
values_hash.each do |k,v|
|
96
|
+
if k == values_hash.keys.last
|
97
|
+
c.p "[#{v}] => #{k}."
|
98
|
+
else
|
99
|
+
c.p "[#{v}] => #{k},"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
broken_cell_para
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module DatabaseDocumenter
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :skipped_modules, :hidden_values_columns, :database_configuration, :footer
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@skipped_modules = []
|
7
|
+
@hidden_values_columns = ['encrypted_password', 'current_sign_in_ip', 'remote_address', 'last_sign_in_ip']
|
8
|
+
@database_configuration = Rails.application.config.database_configuration[Rails.env]
|
9
|
+
@footer = ''
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module DatabaseDocumenter
|
2
|
+
module DatabaseComment
|
3
|
+
def self.get_comment_class(adapter = get_current_adapter)
|
4
|
+
if adapter == "postgresql"
|
5
|
+
DatabaseDocumenter::DatabaseComment::PostgresDatabaseComment
|
6
|
+
else
|
7
|
+
DatabaseDocumenter::DatabaseComment::MysqlDatabaseComment
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.get_current_adapter
|
12
|
+
DatabaseDocumenter.configuration.database_configuration['adapter']
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# A base class for reading column database comments
|
3
|
+
class DatabaseDocumenter::DatabaseComment::BaseDatabaseComment
|
4
|
+
|
5
|
+
def self.read_columns_comment(table_name)
|
6
|
+
raise NotImplementedError
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.read_table_comment(table_name)
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.database_name
|
14
|
+
Rails.application.config.database_configuration[Rails.env]['database'].freeze
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module DatabaseDocumenter
|
2
|
+
class DatabaseComment::MysqlDatabaseComment < DatabaseComment::BaseDatabaseComment
|
3
|
+
|
4
|
+
def self.read_columns_comment(table_name)
|
5
|
+
|
6
|
+
select_comment = <<-SQL
|
7
|
+
SELECT `column_name`, `column_comment`
|
8
|
+
FROM `information_schema`.`COLUMNS`
|
9
|
+
WHERE `table_name` = '#{table_name}'
|
10
|
+
AND `table_schema` = '#{database_name}'
|
11
|
+
AND `column_comment` != '';
|
12
|
+
SQL
|
13
|
+
|
14
|
+
ActiveRecord::Base.connection.execute(select_comment).to_h
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.read_table_comment(table_name)
|
18
|
+
select_comment = <<-SQL
|
19
|
+
SELECT `TABLE_COMMENT`
|
20
|
+
FROM `information_schema`.`TABLES`
|
21
|
+
WHERE `TABLE_NAME` = '#{table_name}'
|
22
|
+
AND `table_schema` = '#{database_name}';
|
23
|
+
SQL
|
24
|
+
|
25
|
+
ActiveRecord::Base.connection.execute(select_comment).to_a.try('[]', 0).try('[]', 0)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module DatabaseDocumenter
|
2
|
+
class DatabaseComment::PostgresDatabaseComment < DatabaseComment::BaseDatabaseComment
|
3
|
+
|
4
|
+
def self.read_columns_comment(table_name)
|
5
|
+
|
6
|
+
select_comment = <<-SQL
|
7
|
+
SELECT
|
8
|
+
cols.column_name,
|
9
|
+
(
|
10
|
+
SELECT
|
11
|
+
pg_catalog.col_description(c.oid, cols.ordinal_position::int)
|
12
|
+
FROM
|
13
|
+
pg_catalog.pg_class c
|
14
|
+
WHERE
|
15
|
+
c.oid = (SELECT ('"' || cols.table_name || '"')::regclass::oid)
|
16
|
+
AND c.relname = cols.table_name
|
17
|
+
) AS column_comment
|
18
|
+
FROM
|
19
|
+
information_schema.columns cols
|
20
|
+
WHERE
|
21
|
+
cols.table_catalog = '#{database_name}'
|
22
|
+
AND cols.table_name = '#{table_name}'
|
23
|
+
AND cols.table_schema = 'public';
|
24
|
+
SQL
|
25
|
+
|
26
|
+
columns_comment_hash ={}
|
27
|
+
ActiveRecord::Base.connection.execute(select_comment).map { |c| columns_comment_hash[c['column_name']] = c['column_comment']}
|
28
|
+
columns_comment_hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.read_table_comment(table_name)
|
32
|
+
select_comment = <<-SQL
|
33
|
+
select obj_description('public.#{table_name}'::regclass);
|
34
|
+
SQL
|
35
|
+
|
36
|
+
ActiveRecord::Base.connection.execute(select_comment)[0]['obj_description']
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module DatabaseDocumenter::TablesSql
|
3
|
+
|
4
|
+
def self.generate
|
5
|
+
|
6
|
+
configuration = DatabaseDocumenter.configuration.database_configuration
|
7
|
+
|
8
|
+
tables_sql = generate_sql_file(configuration)
|
9
|
+
|
10
|
+
self.send("process_#{configuration['adapter']}_sql", tables_sql)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.generate_sql_file(configuration)
|
14
|
+
ActiveRecord::Tasks::DatabaseTasks.structure_dump(configuration, 'database.sql')
|
15
|
+
tables_sql = IO.read('database.sql')
|
16
|
+
File.delete('database.sql')
|
17
|
+
tables_sql
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.process_postgresql_sql(tables_sql)
|
21
|
+
tables_sql_hash = {}
|
22
|
+
tables_sql = tables_sql.split('--').select { |line| line.match(/CREATE TABLE/)}
|
23
|
+
|
24
|
+
tables_sql.each do |sql_statement|
|
25
|
+
key = sql_statement.scan(/public.(.*) \(/)[0][0]
|
26
|
+
broken_cell_para = Caracal::Core::Models::TableCellModel.new do |c|
|
27
|
+
sql_statement.strip.squeeze(' ').split("\n").each do |p|
|
28
|
+
c.p p
|
29
|
+
end
|
30
|
+
end
|
31
|
+
tables_sql_hash[key] = broken_cell_para
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
tables_sql_hash
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.process_mysql2_sql(tables_sql)
|
39
|
+
tables_sql_hash = {}
|
40
|
+
tables_sql = tables_sql.split(';').select { |line| line.match(/CREATE/)}
|
41
|
+
|
42
|
+
tables_sql.each do |sql_statement|
|
43
|
+
key = sql_statement.scan(/`(.*)`/)[0][0]
|
44
|
+
broken_cell_para = Caracal::Core::Models::TableCellModel.new do |c|
|
45
|
+
sql_statement[1..-1].squeeze(' ').split("\n").each do |p|
|
46
|
+
c.p p
|
47
|
+
end
|
48
|
+
end
|
49
|
+
tables_sql_hash[key] = broken_cell_para
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
tables_sql_hash
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "database_documenter"
|
3
|
+
|
4
|
+
task generate_db_document: :environment do
|
5
|
+
Rails.application.eager_load!
|
6
|
+
|
7
|
+
tables_sql = DatabaseDocumenter::TablesSql.generate
|
8
|
+
|
9
|
+
Caracal::Document.save 'database.docx' do |docx|
|
10
|
+
database_comment_class = DatabaseDocumenter::DatabaseComment.get_comment_class
|
11
|
+
|
12
|
+
docx.page_numbers true do
|
13
|
+
align 'center'
|
14
|
+
label "#{DatabaseDocumenter.configuration.footer}"
|
15
|
+
end
|
16
|
+
|
17
|
+
docx.h1 "Database Design"
|
18
|
+
docx.p "The database design specifies how the data of the software is going to be stored."
|
19
|
+
|
20
|
+
printed_tables = []
|
21
|
+
generated_col_description = 0
|
22
|
+
col_description_from_comments = 0
|
23
|
+
ActiveRecord::Base.descendants.each do |klass|
|
24
|
+
next if (klass.class_name != klass.base_class.class_name) || klass.abstract_class? || klass == ActiveAdmin::Comment # Ignore STI classes
|
25
|
+
|
26
|
+
next if printed_tables.include? klass.table_name # Skip duplicate tables in case of has_and_belongs_to_many
|
27
|
+
|
28
|
+
next if DatabaseDocumenter.configuration.skipped_modules.include? klass.parent.name
|
29
|
+
|
30
|
+
printed_tables << klass.table_name
|
31
|
+
|
32
|
+
table_comment = database_comment_class.read_table_comment(klass.table_name)
|
33
|
+
|
34
|
+
docx.p ''
|
35
|
+
docx.h2 "#{klass.table_name} schema"
|
36
|
+
docx.hr
|
37
|
+
|
38
|
+
table_name = ["Table Name", klass.table_name]
|
39
|
+
description = ["Description", (table_comment.nil? || table_comment.empty?) ? "A collection of data related to #{klass.table_name.titleize}" : table_comment ]
|
40
|
+
primary_key = ["Primary Key", klass.primary_key]
|
41
|
+
sql_code = ["SQL Code", tables_sql[klass.table_name]]
|
42
|
+
|
43
|
+
docx.table [table_name, description, primary_key, sql_code], border_size: 4 do
|
44
|
+
cell_style rows[0][0], background: 'b4b4b4', bold: true, width: 2000
|
45
|
+
cell_style rows[1][0], background: 'e0e0e0', bold: true, width: 2000
|
46
|
+
cell_style rows[2][0], background: 'e0e0e0', bold: true, width: 2000
|
47
|
+
cell_style rows[3][0], background: 'e0e0e0', bold: true, width: 2000
|
48
|
+
end
|
49
|
+
|
50
|
+
columns_header = ["Attribute", "Description", "Type", "Example of values"]
|
51
|
+
columns = []
|
52
|
+
sample_record = klass.first
|
53
|
+
columns_comments = database_comment_class.read_columns_comment(klass.table_name)
|
54
|
+
klass.columns.each do |col|
|
55
|
+
column_data = [col.name]
|
56
|
+
if columns_comments[col.name].present?
|
57
|
+
col_description_from_comments +=1
|
58
|
+
broken_cell_para = Caracal::Core::Models::TableCellModel.new do |c|
|
59
|
+
columns_comments[col.name].split("<br/>").each do |s|
|
60
|
+
c.p s
|
61
|
+
end
|
62
|
+
end
|
63
|
+
column_data << broken_cell_para
|
64
|
+
else
|
65
|
+
generated_col_description +=1
|
66
|
+
column_data << DatabaseDocumenter::ColumnDescription.generate(col.name, col.type, klass)
|
67
|
+
end
|
68
|
+
|
69
|
+
column_data << col.type
|
70
|
+
|
71
|
+
hidden_values_columns = DatabaseDocumenter.configuration.hidden_values_columns
|
72
|
+
|
73
|
+
if hidden_values_columns.include?(col.name)
|
74
|
+
column_data << 'Data is hidden/removed'
|
75
|
+
elsif sample_record.nil?
|
76
|
+
column_data << ''
|
77
|
+
else
|
78
|
+
if Rails.version.split(".")[0].to_i == 4
|
79
|
+
column_data << sample_record[col.name]
|
80
|
+
else
|
81
|
+
column_data << sample_record.send("#{col.name}_before_type_cast")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
columns << column_data
|
86
|
+
end
|
87
|
+
docx.page
|
88
|
+
docx.table [columns_header] + columns, border_size: 4 do
|
89
|
+
cell_style rows[0], background: 'e0e0e0', bold: true
|
90
|
+
end
|
91
|
+
docx.page
|
92
|
+
end
|
93
|
+
puts "Number of columns with generated description #{generated_col_description}"
|
94
|
+
puts "Number of columns with description from comments #{col_description_from_comments}"
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
task :generate_dd_initializer do
|
2
|
+
template_path = File.join( File.dirname(__FILE__), 'templates/database_documenter.rb').freeze
|
3
|
+
initializer_path = 'config/initializers/database_documenter.rb'.freeze
|
4
|
+
# Check if the initializer already exists
|
5
|
+
abort("Initializer already exists") if File.file?(initializer_path)
|
6
|
+
|
7
|
+
# Copy the template to the initializers dir
|
8
|
+
FileUtils.cp(template_path, initializer_path)
|
9
|
+
end
|
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: database_documenter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ahmed Yehia
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-07-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.17'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.17'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: caracal
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.4.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.4.1
|
69
|
+
description: Generate Database Documentation as a word document
|
70
|
+
email:
|
71
|
+
- ruby@espace.com.eg
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".rspec"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- Gemfile.lock
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- bin/console
|
84
|
+
- bin/setup
|
85
|
+
- database_documenter.gemspec
|
86
|
+
- lib/database_documenter.rb
|
87
|
+
- lib/database_documenter/column_description.rb
|
88
|
+
- lib/database_documenter/configuration.rb
|
89
|
+
- lib/database_documenter/database_comment.rb
|
90
|
+
- lib/database_documenter/database_comment/base_database_comment.rb
|
91
|
+
- lib/database_documenter/database_comment/mysql_database_comment.rb
|
92
|
+
- lib/database_documenter/database_comment/postgres_database_comment.rb
|
93
|
+
- lib/database_documenter/railtie.rb
|
94
|
+
- lib/database_documenter/tables_sql.rb
|
95
|
+
- lib/database_documenter/version.rb
|
96
|
+
- lib/tasks/generate_db_document.rake
|
97
|
+
- lib/tasks/generate_dd_initializer.rake
|
98
|
+
- lib/tasks/templates/database_documenter.rb
|
99
|
+
homepage: https://github.com/espace/db_documenter
|
100
|
+
licenses: []
|
101
|
+
metadata: {}
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
requirements: []
|
117
|
+
rubygems_version: 3.0.4
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: Generate Database Documentation as a word document
|
121
|
+
test_files: []
|