database_documenter 0.1.6 → 0.1.7
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/.overcommit.yml +37 -0
- data/.rubocop.yml +84 -0
- data/Gemfile.lock +32 -1
- data/README.md +7 -3
- data/Rakefile +1 -1
- data/database_documenter.gemspec +9 -6
- data/lib/database_documenter.rb +3 -1
- data/lib/database_documenter/column_description.rb +33 -33
- data/lib/database_documenter/configuration.rb +1 -1
- data/lib/database_documenter/database_comment.rb +3 -3
- data/lib/database_documenter/database_comment/base_database_comment.rb +11 -10
- data/lib/database_documenter/database_comment/mysql_database_comment.rb +0 -2
- data/lib/database_documenter/database_comment/postgres_database_comment.rb +18 -20
- data/lib/database_documenter/exporters/export_to_word.rb +115 -0
- data/lib/database_documenter/table_data.rb +67 -0
- data/lib/database_documenter/tables_sql.rb +4 -7
- data/lib/database_documenter/version.rb +3 -1
- data/lib/tasks/generate_db_document.rake +1 -91
- data/lib/tasks/generate_dd_initializer.rake +2 -2
- data/lib/tasks/templates/database_documenter.rb +3 -3
- metadata +48 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f61327bfc7acbd2f256ae2ad2c93640eb981e01a88a5fb215ed0da8eee35049b
|
4
|
+
data.tar.gz: 510687dda8d4aefa92660a767687ed19e91dc31379218f1dc91ed63348cbdc8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0269e91c7cac255801c79ddcee20863da8d0b3c1c44ec197b97031dc97dccec92ee09bd9d2dd35e021e8b2fbe5a4d7abf50755e19ad2a37561ab3dd552a8afa7'
|
7
|
+
data.tar.gz: e66e7ccef12797577dd78bcd4b53cb17988fd15baddd3627cf17da9bc53d8d44f1f864e3d461c2553c10c82190ca47eb7c231b06a07fe860ed35b36a6cc2cf0b
|
data/.overcommit.yml
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
gemfile: Gemfile
|
2
|
+
|
3
|
+
PreCommit:
|
4
|
+
BundleCheck:
|
5
|
+
enabled: true
|
6
|
+
|
7
|
+
FixMe:
|
8
|
+
enabled: true
|
9
|
+
keywords: ["FIXME"]
|
10
|
+
exclude:
|
11
|
+
- .overcommit.yml
|
12
|
+
|
13
|
+
LocalPathsInGemfile:
|
14
|
+
enabled: true
|
15
|
+
|
16
|
+
RailsSchemaUpToDate:
|
17
|
+
enabled: false
|
18
|
+
|
19
|
+
RuboCop:
|
20
|
+
enabled: true
|
21
|
+
on_warn: fail
|
22
|
+
|
23
|
+
TrailingWhitespace:
|
24
|
+
enabled: true
|
25
|
+
exclude:
|
26
|
+
- "**/db/structure.sql"
|
27
|
+
|
28
|
+
YamlSyntax:
|
29
|
+
enabled: true
|
30
|
+
|
31
|
+
HardTabs:
|
32
|
+
enabled: true
|
33
|
+
|
34
|
+
PrePush:
|
35
|
+
RSpec:
|
36
|
+
enabled: true
|
37
|
+
command: ['xvfb-run', '-a', 'bundle', 'exec', 'rspec']
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
AllCops:
|
2
|
+
DisplayCopNames: true
|
3
|
+
DisplayStyleGuide: true
|
4
|
+
TargetRubyVersion: 2.5
|
5
|
+
Exclude:
|
6
|
+
- "bin/*"
|
7
|
+
- Gemfile
|
8
|
+
|
9
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
10
|
+
EnforcedStyle: no_space
|
11
|
+
|
12
|
+
Metrics/AbcSize:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Metrics/BlockLength:
|
16
|
+
Exclude:
|
17
|
+
- "spec/**/*"
|
18
|
+
|
19
|
+
Metrics/ClassLength:
|
20
|
+
Exclude:
|
21
|
+
- "spec/**/*"
|
22
|
+
|
23
|
+
Metrics/MethodLength:
|
24
|
+
Max: 30
|
25
|
+
Exclude:
|
26
|
+
- "spec/**/*"
|
27
|
+
|
28
|
+
Metrics/LineLength:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Naming/MemoizedInstanceVariableName:
|
32
|
+
EnforcedStyleForLeadingUnderscores: optional
|
33
|
+
|
34
|
+
Naming/VariableNumber:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Rails:
|
38
|
+
Enabled: true
|
39
|
+
|
40
|
+
Rails/ApplicationRecord:
|
41
|
+
Exclude:
|
42
|
+
- "db/migrate/**"
|
43
|
+
|
44
|
+
Rails/RefuteMethods:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
Rails/Validation:
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
Style/BarePercentLiterals:
|
51
|
+
EnforcedStyle: percent_q
|
52
|
+
|
53
|
+
Style/ClassAndModuleChildren:
|
54
|
+
Enabled: false
|
55
|
+
|
56
|
+
Style/Documentation:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
Style/DoubleNegation:
|
60
|
+
Enabled: false
|
61
|
+
|
62
|
+
Style/EmptyMethod:
|
63
|
+
Enabled: false
|
64
|
+
|
65
|
+
Style/FrozenStringLiteralComment:
|
66
|
+
Enabled: false
|
67
|
+
|
68
|
+
Style/NumericPredicate:
|
69
|
+
Enabled: false
|
70
|
+
|
71
|
+
Style/StringLiterals:
|
72
|
+
Enabled: false
|
73
|
+
|
74
|
+
Style/TrivialAccessors:
|
75
|
+
AllowPredicates: true
|
76
|
+
|
77
|
+
Style/RescueStandardError:
|
78
|
+
Enabled: false
|
79
|
+
|
80
|
+
Rails/HasManyOrHasOneDependent:
|
81
|
+
Enabled: false
|
82
|
+
|
83
|
+
Style/NumericLiterals:
|
84
|
+
Enabled: false
|
data/Gemfile.lock
CHANGED
@@ -1,20 +1,38 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
database_documenter (0.1.
|
4
|
+
database_documenter (0.1.7)
|
5
5
|
caracal (= 1.4.1)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
+
ast (2.4.0)
|
10
11
|
caracal (1.4.1)
|
11
12
|
nokogiri (~> 1.6)
|
12
13
|
rubyzip (~> 1.1)
|
13
14
|
tilt (>= 1.4)
|
15
|
+
childprocess (1.0.1)
|
16
|
+
rake (< 13.0)
|
17
|
+
coderay (1.1.2)
|
14
18
|
diff-lcs (1.3)
|
19
|
+
iniparse (1.4.4)
|
20
|
+
jaro_winkler (1.5.3)
|
21
|
+
method_source (0.9.2)
|
15
22
|
mini_portile2 (2.4.0)
|
16
23
|
nokogiri (1.10.3)
|
17
24
|
mini_portile2 (~> 2.4.0)
|
25
|
+
overcommit (0.49.0)
|
26
|
+
childprocess (>= 0.6.3, < 2.0)
|
27
|
+
iniparse (~> 1.4)
|
28
|
+
parallel (1.17.0)
|
29
|
+
parser (2.6.3.0)
|
30
|
+
ast (~> 2.4.0)
|
31
|
+
powerpack (0.1.2)
|
32
|
+
pry (0.12.2)
|
33
|
+
coderay (~> 1.1.0)
|
34
|
+
method_source (~> 0.9.0)
|
35
|
+
rainbow (3.0.0)
|
18
36
|
rake (10.5.0)
|
19
37
|
rspec (3.8.0)
|
20
38
|
rspec-core (~> 3.8.0)
|
@@ -29,8 +47,18 @@ GEM
|
|
29
47
|
diff-lcs (>= 1.2.0, < 2.0)
|
30
48
|
rspec-support (~> 3.8.0)
|
31
49
|
rspec-support (3.8.2)
|
50
|
+
rubocop (0.58.2)
|
51
|
+
jaro_winkler (~> 1.5.1)
|
52
|
+
parallel (~> 1.10)
|
53
|
+
parser (>= 2.5, != 2.5.1.1)
|
54
|
+
powerpack (~> 0.1)
|
55
|
+
rainbow (>= 2.2.2, < 4.0)
|
56
|
+
ruby-progressbar (~> 1.7)
|
57
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
58
|
+
ruby-progressbar (1.10.1)
|
32
59
|
rubyzip (1.2.3)
|
33
60
|
tilt (2.0.9)
|
61
|
+
unicode-display_width (1.6.0)
|
34
62
|
|
35
63
|
PLATFORMS
|
36
64
|
ruby
|
@@ -38,8 +66,11 @@ PLATFORMS
|
|
38
66
|
DEPENDENCIES
|
39
67
|
bundler (~> 1.17)
|
40
68
|
database_documenter!
|
69
|
+
overcommit (= 0.49.0)
|
70
|
+
pry (~> 0.12.2)
|
41
71
|
rake (~> 10.0)
|
42
72
|
rspec (~> 3.0)
|
73
|
+
rubocop (= 0.58.2)
|
43
74
|
|
44
75
|
BUNDLED WITH
|
45
76
|
1.17.3
|
data/README.md
CHANGED
@@ -52,10 +52,14 @@ use [migration comments](https://github.com/pinnymz/migration_comments) gem or [
|
|
52
52
|
### Rails 5.2
|
53
53
|
use `change_column_comment` and `change_table_comment` methods in rails 5
|
54
54
|
|
55
|
+
## Contribution
|
56
|
+
|
57
|
+
- Fork & create a branch
|
58
|
+
- bundle install
|
59
|
+
- Make sure to run `overcommit --install` before working to run RuboCop before push.
|
60
|
+
- Create Pull Request.
|
61
|
+
|
55
62
|
## TODO
|
56
63
|
|
57
|
-
- Add more Configurations.
|
58
|
-
- Add OverCommit
|
59
64
|
- Generate the ERD with the file.
|
60
|
-
- Clean code.
|
61
65
|
- Add test cases.
|
data/Rakefile
CHANGED
data/database_documenter.gemspec
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require "database_documenter/version"
|
5
4
|
|
@@ -8,14 +7,14 @@ Gem::Specification.new do |spec|
|
|
8
7
|
spec.version = DatabaseDocumenter::VERSION
|
9
8
|
spec.authors = ["Ahmed Yehia"]
|
10
9
|
spec.email = ["ruby@espace.com.eg"]
|
11
|
-
spec.homepage
|
10
|
+
spec.homepage = "https://github.com/espace/db_documenter"
|
12
11
|
|
13
|
-
spec.summary =
|
14
|
-
spec.description =
|
12
|
+
spec.summary = 'Generate Database Documentation as a word document'
|
13
|
+
spec.description = 'Generate Database Documentation as a word document'
|
15
14
|
|
16
15
|
# Specify which files should be added to the gem when it is released.
|
17
16
|
# 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(
|
17
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
19
18
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
19
|
end
|
21
20
|
spec.bindir = "exe"
|
@@ -23,7 +22,11 @@ Gem::Specification.new do |spec|
|
|
23
22
|
spec.require_paths = ["lib"]
|
24
23
|
|
25
24
|
spec.add_development_dependency "bundler", "~> 1.17"
|
25
|
+
spec.add_development_dependency "overcommit", "=0.49.0"
|
26
|
+
spec.add_development_dependency 'pry', '~> 0.12.2'
|
26
27
|
spec.add_development_dependency "rake", "~> 10.0"
|
27
28
|
spec.add_development_dependency "rspec", "~> 3.0"
|
29
|
+
spec.add_development_dependency "rubocop", "=0.58.2"
|
30
|
+
|
28
31
|
spec.add_dependency 'caracal', '=1.4.1'
|
29
32
|
end
|
data/lib/database_documenter.rb
CHANGED
@@ -6,13 +6,15 @@ require 'database_documenter/database_comment/mysql_database_comment'
|
|
6
6
|
require 'database_documenter/database_comment/postgres_database_comment'
|
7
7
|
require 'database_documenter/tables_sql'
|
8
8
|
require 'database_documenter/column_description'
|
9
|
+
require 'database_documenter/table_data'
|
10
|
+
require 'database_documenter/exporters/export_to_word'
|
9
11
|
require 'caracal'
|
10
12
|
require "database_documenter/railtie" if defined?(Rails)
|
11
13
|
|
12
14
|
module DatabaseDocumenter
|
13
15
|
class Error < StandardError; end
|
14
16
|
class << self
|
15
|
-
|
17
|
+
attr_writer :configuration
|
16
18
|
end
|
17
19
|
|
18
20
|
def self.configuration
|
@@ -1,36 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module DatabaseDocumenter::ColumnDescription
|
3
2
|
|
3
|
+
module DatabaseDocumenter::ColumnDescription
|
4
4
|
def self.generate(column_name, column_type, klass)
|
5
5
|
klass_name = klass.name.demodulize.titleize
|
6
6
|
|
7
|
-
#
|
8
|
-
if klass.defined_enums.
|
9
|
-
return generate_enum_column_description(column_name, column_type, klass_name, klass)
|
10
|
-
end
|
7
|
+
# Handle enums
|
8
|
+
return generate_enum_column_description(column_name, column_type, klass_name, klass) if klass.defined_enums.key?(column_name)
|
11
9
|
|
12
|
-
#
|
13
|
-
if klass.respond_to?(:aasm) && klass.aasm.attribute_name.to_s == column_name
|
14
|
-
|
15
|
-
|
10
|
+
# Handle assm
|
11
|
+
return generate_assm_column_description(column_name, column_type, klass_name, klass) if klass.respond_to?(:aasm) && klass.aasm.attribute_name.to_s == column_name
|
12
|
+
|
13
|
+
unless klass.subclasses.select { |x| x.respond_to?(:aasm) }[0].nil?
|
16
14
|
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
|
15
|
+
return generate_assm_column_description(column_name, column_type, klass_name, subklass) if subklass.aasm.attribute_name.to_s == column_name
|
20
16
|
end
|
21
17
|
|
22
18
|
# Default
|
23
|
-
|
19
|
+
generate_default_column_descrription(column_name, column_type, klass_name, klass)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.generate_default_column_descrription(column_name, column_type, klass_name, klass)
|
23
|
+
if respond_to?("generate_#{column_type}_column_description")
|
24
24
|
send("generate_#{column_type}_column_description", column_name, column_type, klass_name)
|
25
25
|
else
|
26
|
-
|
26
|
+
generate_with_default_rules(column_name, column_type, klass_name, klass)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
def self.
|
30
|
+
def self.handle_enums(column_name, column_type, klass_name, klass)
|
31
|
+
generate_enum_column_description(column_name, column_type, klass_name, klass)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.generate_datetime_column_description(column_name, _column_type, _klass_name)
|
31
35
|
case column_name
|
32
|
-
when 'remember_created_at'
|
33
|
-
"Date when remember me created"
|
34
36
|
when 'reset_password_sent_at'
|
35
37
|
"Date when reset password sent"
|
36
38
|
when 'current_sign_in_at'
|
@@ -46,7 +48,7 @@ module DatabaseDocumenter::ColumnDescription
|
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
def self.generate_boolean_column_description(column_name,
|
51
|
+
def self.generate_boolean_column_description(column_name, _column_type, klass_name)
|
50
52
|
case column_name
|
51
53
|
when /.*active/, /has_.*/, "canceled"
|
52
54
|
"Is #{klass_name.titlecase.downcase} #{column_name.titlecase.downcase}"
|
@@ -57,10 +59,10 @@ module DatabaseDocumenter::ColumnDescription
|
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
60
|
-
def self.generate_with_default_rules(column_name,
|
62
|
+
def self.generate_with_default_rules(column_name, _column_type, klass_name, klass)
|
61
63
|
case column_name
|
62
64
|
when 'type'
|
63
|
-
values_hash = Hash[klass.subclasses.collect { |k| [k.name.underscore.humanize, k.name] }
|
65
|
+
values_hash = Hash[klass.subclasses.collect { |k| [k.name.underscore.humanize, k.name] }]
|
64
66
|
represent_multi_value_column(column_name, klass_name, values_hash)
|
65
67
|
when /.*_code/
|
66
68
|
refered_table_name = column_name.scan(/(.*)_code/)[0][0]
|
@@ -79,27 +81,25 @@ module DatabaseDocumenter::ColumnDescription
|
|
79
81
|
end
|
80
82
|
end
|
81
83
|
|
82
|
-
def self.generate_enum_column_description(column_name,
|
84
|
+
def self.generate_enum_column_description(column_name, _column_type, klass_name, klass)
|
83
85
|
values_hash = klass.defined_enums[column_name]
|
84
86
|
represent_multi_value_column(column_name, klass_name, values_hash)
|
85
87
|
end
|
86
88
|
|
87
|
-
def self.generate_assm_column_description(column_name,
|
88
|
-
values_hash = Hash[klass.aasm.states.collect { |k| [k.name.to_s.humanize, k.name.to_s] }
|
89
|
+
def self.generate_assm_column_description(column_name, _column_type, klass_name, klass)
|
90
|
+
values_hash = Hash[klass.aasm.states.collect { |k| [k.name.to_s.humanize, k.name.to_s] }]
|
89
91
|
represent_multi_value_column(column_name, klass_name, values_hash)
|
90
92
|
end
|
91
93
|
|
92
94
|
def self.represent_multi_value_column(column_name, klass_name, values_hash)
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
101
|
-
end
|
95
|
+
description = ["#{column_name.titlecase.downcase} of #{klass_name.titleize}, possible values:"]
|
96
|
+
values_hash.each do |k, v|
|
97
|
+
description << if k == values_hash.keys.last
|
98
|
+
"[#{v}] => #{k}."
|
99
|
+
else
|
100
|
+
"[#{v}] => #{k},"
|
101
|
+
end
|
102
102
|
end
|
103
|
-
|
103
|
+
description
|
104
104
|
end
|
105
105
|
end
|
@@ -4,7 +4,7 @@ module DatabaseDocumenter
|
|
4
4
|
|
5
5
|
def initialize
|
6
6
|
@skipped_modules = []
|
7
|
-
@hidden_values_columns = [
|
7
|
+
@hidden_values_columns = %w[encrypted_password current_sign_in_ip remote_address last_sign_in_ip]
|
8
8
|
@database_configuration = Rails.application.config.database_configuration[Rails.env]
|
9
9
|
@footer = ''
|
10
10
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module DatabaseDocumenter
|
2
2
|
module DatabaseComment
|
3
|
-
def self.get_comment_class(adapter
|
4
|
-
if
|
3
|
+
def self.get_comment_class(adapter=current_adapter)
|
4
|
+
if adapter == "postgresql"
|
5
5
|
DatabaseDocumenter::DatabaseComment::PostgresDatabaseComment
|
6
6
|
else
|
7
7
|
DatabaseDocumenter::DatabaseComment::MysqlDatabaseComment
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
def self.
|
11
|
+
def self.current_adapter
|
12
12
|
DatabaseDocumenter.configuration.database_configuration['adapter']
|
13
13
|
end
|
14
14
|
end
|
@@ -1,16 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
# A base class for reading column database comments
|
3
|
-
class DatabaseDocumenter::DatabaseComment::BaseDatabaseComment
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
class DatabaseDocumenter::DatabaseComment::BaseDatabaseComment
|
6
|
+
def self.read_columns_comment(_table_name)
|
7
|
+
raise NotImplementedError
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
def self.read_table_comment(_table_name)
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
14
|
+
def self.database_name
|
15
|
+
Rails.application.config.database_configuration[Rails.env]['database'].freeze
|
16
16
|
end
|
17
|
+
end
|
@@ -1,30 +1,28 @@
|
|
1
1
|
module DatabaseDocumenter
|
2
2
|
class DatabaseComment::PostgresDatabaseComment < DatabaseComment::BaseDatabaseComment
|
3
|
-
|
4
3
|
def self.read_columns_comment(table_name)
|
5
|
-
|
6
4
|
select_comment = <<-SQL
|
7
5
|
SELECT
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
6
|
+
cols.column_name,
|
7
|
+
(
|
8
|
+
SELECT
|
9
|
+
pg_catalog.col_description(c.oid, cols.ordinal_position::int)
|
10
|
+
FROM
|
11
|
+
pg_catalog.pg_class c
|
12
|
+
WHERE
|
13
|
+
c.oid = (SELECT ('"' || cols.table_name || '"')::regclass::oid)
|
14
|
+
AND c.relname = cols.table_name
|
15
|
+
) AS column_comment
|
16
|
+
FROM
|
17
|
+
information_schema.columns cols
|
18
|
+
WHERE
|
19
|
+
cols.table_catalog = '#{database_name}'
|
20
|
+
AND cols.table_name = '#{table_name}'
|
21
|
+
AND cols.table_schema = 'public';
|
24
22
|
SQL
|
25
23
|
|
26
|
-
columns_comment_hash ={}
|
27
|
-
ActiveRecord::Base.connection.execute(select_comment).map { |c| columns_comment_hash[c['column_name']] = c['column_comment']}
|
24
|
+
columns_comment_hash = {}
|
25
|
+
ActiveRecord::Base.connection.execute(select_comment).map { |c| columns_comment_hash[c['column_name']] = c['column_comment'] }
|
28
26
|
columns_comment_hash
|
29
27
|
end
|
30
28
|
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DatabaseDocumenter::Exporters
|
4
|
+
class ExportToWord
|
5
|
+
attr_accessor :table_data, :printed_tables, :generated_cols_count, :commented_cols_count
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
self.table_data = DatabaseDocumenter::TableData.new
|
9
|
+
self.printed_tables = []
|
10
|
+
self.generated_cols_count = 0
|
11
|
+
self.commented_cols_count = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
load_all_models
|
16
|
+
generate_word_document
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_all_models
|
20
|
+
Rails.application.eager_load!
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate_word_document
|
24
|
+
Caracal::Document.save 'database.docx' do |docx|
|
25
|
+
add_word_header(docx)
|
26
|
+
add_word_footer(docx)
|
27
|
+
|
28
|
+
ActiveRecord::Base.descendants.each do |klass|
|
29
|
+
# Skip STI classes
|
30
|
+
# Skip duplicate tables in case of has_and_belongs_to_many
|
31
|
+
# Skip certain modules
|
32
|
+
next if skip_class?(klass)
|
33
|
+
|
34
|
+
printed_tables << klass.table_name
|
35
|
+
|
36
|
+
generate_table_metadata(docx, klass)
|
37
|
+
generate_table_columns(docx, klass)
|
38
|
+
|
39
|
+
docx.page
|
40
|
+
end
|
41
|
+
end
|
42
|
+
Rails.logger.info "Number of columns with generated description #{generated_cols_count}"
|
43
|
+
Rails.logger.info "Number of columns with description from comments #{commented_cols_count}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_word_footer(docx)
|
47
|
+
docx.page_numbers true do
|
48
|
+
align 'center'
|
49
|
+
label DatabaseDocumenter.configuration.footer.to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_word_header(docx)
|
54
|
+
docx.h1 "Database Design"
|
55
|
+
docx.p "The database design specifies how the data of the software is going to be stored."
|
56
|
+
end
|
57
|
+
|
58
|
+
def skip_class?(klass)
|
59
|
+
(klass.class_name != klass.base_class.class_name) || klass.abstract_class? || (klass == ActiveAdmin::Comment) ||
|
60
|
+
(printed_tables.include? klass.table_name) || (DatabaseDocumenter.configuration.skipped_modules.include? klass.parent.name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_table_metadata(docx, klass)
|
64
|
+
metadata = table_data.get_meta_data(klass)
|
65
|
+
docx.p ''
|
66
|
+
docx.h2 "#{klass.table_name} schema"
|
67
|
+
docx.hr
|
68
|
+
|
69
|
+
word_table = [
|
70
|
+
["Table Name", metadata[:name]],
|
71
|
+
["Description", metadata[:description]],
|
72
|
+
["Primary Key", metadata[:primary_key]],
|
73
|
+
["SQL Code", metadata[:sql_code]]
|
74
|
+
]
|
75
|
+
|
76
|
+
docx.table word_table, border_size: 4 do
|
77
|
+
cell_style rows[0][0], background: 'b4b4b4', bold: true, width: 2000
|
78
|
+
cell_style rows[1][0], background: 'e0e0e0', bold: true, width: 2000
|
79
|
+
cell_style rows[2][0], background: 'e0e0e0', bold: true, width: 2000
|
80
|
+
cell_style rows[3][0], background: 'e0e0e0', bold: true, width: 2000
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_table_columns(docx, klass)
|
85
|
+
columns_header = ["Attribute", "Description", "Type", "Example of values"]
|
86
|
+
columns = []
|
87
|
+
|
88
|
+
columns_data = table_data.get_columns_data(klass)
|
89
|
+
|
90
|
+
columns_data.each do |col|
|
91
|
+
data = [col[:name]]
|
92
|
+
if col[:description_generated]
|
93
|
+
self.generated_cols_count += 1
|
94
|
+
else
|
95
|
+
self.commented_cols_count += 1
|
96
|
+
end
|
97
|
+
|
98
|
+
broken_cell_para = Caracal::Core::Models::TableCellModel.new do |c|
|
99
|
+
col[:description].flatten.each do |s|
|
100
|
+
c.p s
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
data << broken_cell_para
|
105
|
+
data << col[:type]
|
106
|
+
data << col[:value]
|
107
|
+
columns << data
|
108
|
+
end
|
109
|
+
docx.page
|
110
|
+
docx.table [columns_header] + columns, border_size: 4 do
|
111
|
+
cell_style rows[0], background: 'e0e0e0', bold: true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DatabaseDocumenter::TableData
|
4
|
+
attr_accessor :tables_sql, :database_comment_class
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
self.database_comment_class = DatabaseDocumenter::DatabaseComment.get_comment_class
|
8
|
+
load_tables_sql_data
|
9
|
+
end
|
10
|
+
|
11
|
+
def load_tables_sql_data
|
12
|
+
self.tables_sql = DatabaseDocumenter::TablesSql.generate
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_meta_data(klass)
|
16
|
+
table_comment = database_comment_class.read_table_comment(klass.table_name)
|
17
|
+
|
18
|
+
data = {}
|
19
|
+
data[:name] = klass.table_name
|
20
|
+
data[:description] = table_comment.presence || "A collection of data related to #{klass.table_name.titleize}"
|
21
|
+
data[:primary_key] = klass.primary_key
|
22
|
+
data[:sql_code] = tables_sql[klass.table_name]
|
23
|
+
data
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_columns_data(klass)
|
27
|
+
sample_record = klass.first
|
28
|
+
columns_comments = database_comment_class.read_columns_comment(klass.table_name)
|
29
|
+
columns = []
|
30
|
+
klass.columns.each do |col|
|
31
|
+
column_data = get_column_data(klass, col, sample_record, columns_comments[col.name])
|
32
|
+
|
33
|
+
columns << column_data
|
34
|
+
end
|
35
|
+
columns
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_column_data(klass, col, sample_record, column_comment)
|
39
|
+
column_data = { name: col.name }
|
40
|
+
column_data[:description] = []
|
41
|
+
|
42
|
+
if column_comment.present?
|
43
|
+
column_data[:description_generated] = false
|
44
|
+
column_comment.split("<br/>").each do |s|
|
45
|
+
column_data[:description] << s
|
46
|
+
end
|
47
|
+
else
|
48
|
+
column_data[:description_generated] = true
|
49
|
+
column_data[:description] << DatabaseDocumenter::ColumnDescription.generate(col.name, col.type, klass)
|
50
|
+
end
|
51
|
+
|
52
|
+
column_data[:type] = col.type
|
53
|
+
|
54
|
+
hidden_values_columns = DatabaseDocumenter.configuration.hidden_values_columns
|
55
|
+
|
56
|
+
column_data[:value] = if hidden_values_columns.include?(col.name)
|
57
|
+
'Data is hidden/removed'
|
58
|
+
elsif sample_record.nil?
|
59
|
+
''
|
60
|
+
elsif Rails.version.split(".")[0].to_i == 4
|
61
|
+
sample_record[col.name]
|
62
|
+
else
|
63
|
+
sample_record.send("#{col.name}_before_type_cast")
|
64
|
+
end
|
65
|
+
column_data
|
66
|
+
end
|
67
|
+
end
|
@@ -1,13 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module DatabaseDocumenter::TablesSql
|
3
2
|
|
3
|
+
module DatabaseDocumenter::TablesSql
|
4
4
|
def self.generate
|
5
|
-
|
6
5
|
configuration = DatabaseDocumenter.configuration.database_configuration
|
7
6
|
|
8
7
|
tables_sql = generate_sql_file(configuration)
|
9
8
|
|
10
|
-
|
9
|
+
send("process_#{configuration['adapter']}_sql", tables_sql)
|
11
10
|
end
|
12
11
|
|
13
12
|
def self.generate_sql_file(configuration)
|
@@ -19,7 +18,7 @@ module DatabaseDocumenter::TablesSql
|
|
19
18
|
|
20
19
|
def self.process_postgresql_sql(tables_sql)
|
21
20
|
tables_sql_hash = {}
|
22
|
-
tables_sql = tables_sql.split('--').select { |line| line.match(/CREATE TABLE/)}
|
21
|
+
tables_sql = tables_sql.split('--').select { |line| line.match(/CREATE TABLE/) }
|
23
22
|
|
24
23
|
tables_sql.each do |sql_statement|
|
25
24
|
key = sql_statement.scan(/public.(.*) \(/)[0][0]
|
@@ -29,7 +28,6 @@ module DatabaseDocumenter::TablesSql
|
|
29
28
|
end
|
30
29
|
end
|
31
30
|
tables_sql_hash[key] = broken_cell_para
|
32
|
-
|
33
31
|
end
|
34
32
|
|
35
33
|
tables_sql_hash
|
@@ -37,7 +35,7 @@ module DatabaseDocumenter::TablesSql
|
|
37
35
|
|
38
36
|
def self.process_mysql2_sql(tables_sql)
|
39
37
|
tables_sql_hash = {}
|
40
|
-
tables_sql = tables_sql.split(';').select { |line| line.match(/CREATE/)}
|
38
|
+
tables_sql = tables_sql.split(';').select { |line| line.match(/CREATE/) }
|
41
39
|
|
42
40
|
tables_sql.each do |sql_statement|
|
43
41
|
key = sql_statement.scan(/`(.*)`/)[0][0]
|
@@ -47,7 +45,6 @@ module DatabaseDocumenter::TablesSql
|
|
47
45
|
end
|
48
46
|
end
|
49
47
|
tables_sql_hash[key] = broken_cell_para
|
50
|
-
|
51
48
|
end
|
52
49
|
|
53
50
|
tables_sql_hash
|
@@ -2,95 +2,5 @@ require "bundler/setup"
|
|
2
2
|
require "database_documenter"
|
3
3
|
|
4
4
|
task generate_db_document: :environment do
|
5
|
-
|
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
|
5
|
+
DatabaseDocumenter::Exporters::ExportToWord.new.call
|
96
6
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
task :generate_dd_initializer do
|
2
|
-
template_path = File.join(
|
2
|
+
template_path = File.join(File.dirname(__FILE__), 'templates/database_documenter.rb').freeze
|
3
3
|
initializer_path = 'config/initializers/database_documenter.rb'.freeze
|
4
4
|
# Check if the initializer already exists
|
5
5
|
abort("Initializer already exists") if File.file?(initializer_path)
|
6
6
|
|
7
7
|
# Copy the template to the initializers dir
|
8
8
|
FileUtils.cp(template_path, initializer_path)
|
9
|
-
end
|
9
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
DatabaseDocumenter.configure do |config|
|
2
|
-
config.skipped_modules = %w(NAMESPACE)
|
3
|
-
config.hidden_values_columns = %w(col1 col2)
|
4
|
-
config.footer = "Generated by Company" # Footer beside the pagination
|
2
|
+
# config.skipped_modules = %w(NAMESPACE)
|
3
|
+
# config.hidden_values_columns = %w(col1 col2)
|
4
|
+
# config.footer = "Generated by Company" # Footer beside the pagination
|
5
5
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: database_documenter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ahmed Yehia
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-07-
|
11
|
+
date: 2019-07-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -24,6 +24,34 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.17'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: overcommit
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.49.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.49.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.12.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.12.2
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: rake
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +80,20 @@ dependencies:
|
|
52
80
|
- - "~>"
|
53
81
|
- !ruby/object:Gem::Version
|
54
82
|
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.58.2
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.58.2
|
55
97
|
- !ruby/object:Gem::Dependency
|
56
98
|
name: caracal
|
57
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,7 +116,9 @@ extensions: []
|
|
74
116
|
extra_rdoc_files: []
|
75
117
|
files:
|
76
118
|
- ".gitignore"
|
119
|
+
- ".overcommit.yml"
|
77
120
|
- ".rspec"
|
121
|
+
- ".rubocop.yml"
|
78
122
|
- ".travis.yml"
|
79
123
|
- Gemfile
|
80
124
|
- Gemfile.lock
|
@@ -90,7 +134,9 @@ files:
|
|
90
134
|
- lib/database_documenter/database_comment/base_database_comment.rb
|
91
135
|
- lib/database_documenter/database_comment/mysql_database_comment.rb
|
92
136
|
- lib/database_documenter/database_comment/postgres_database_comment.rb
|
137
|
+
- lib/database_documenter/exporters/export_to_word.rb
|
93
138
|
- lib/database_documenter/railtie.rb
|
139
|
+
- lib/database_documenter/table_data.rb
|
94
140
|
- lib/database_documenter/tables_sql.rb
|
95
141
|
- lib/database_documenter/version.rb
|
96
142
|
- lib/tasks/generate_db_document.rake
|