database_documenter 0.1.6 → 0.1.7

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
  SHA256:
3
- metadata.gz: 79f3c7ce476383f98904cc065f592a2bfc522507b0bb5e9777b98429f206fde1
4
- data.tar.gz: 52c6cebb47928cb00fc4a1e7f94aa6b807709970e78b3cb38529a7eff34e896c
3
+ metadata.gz: f61327bfc7acbd2f256ae2ad2c93640eb981e01a88a5fb215ed0da8eee35049b
4
+ data.tar.gz: 510687dda8d4aefa92660a767687ed19e91dc31379218f1dc91ed63348cbdc8e
5
5
  SHA512:
6
- metadata.gz: 70679fc84a3e1e0b9e143c0f642d78ed6541cb899e07d7329db83399e5e93788eab57e6dda6b32a8e5236132ff31caeed17545a9c2554ff5f9b200ab85adacf8
7
- data.tar.gz: 3fd5a31dd095a61c12c40c3c701bb683f47be404e18d6018308d3dbc956b3bff65caafd68193479ecd73bf5371d32487de2d2f4ef46f6df09ae739f1830b45de
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.5)
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
@@ -3,4 +3,4 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
@@ -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 = "https://github.com/espace/db_documenter"
10
+ spec.homepage = "https://github.com/espace/db_documenter"
12
11
 
13
- spec.summary = %q{Generate Database Documentation as a word document}
14
- spec.description = %q{Generate Database Documentation as a word document}
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('..', __FILE__)) do
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
@@ -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
- attr_accessor :configuration
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
- # 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
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
- # 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
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
- if self.respond_to?("generate_#{column_type}_column_description")
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
- self.generate_with_default_rules(column_name, column_type, klass_name, klass)
26
+ generate_with_default_rules(column_name, column_type, klass_name, klass)
27
27
  end
28
28
  end
29
29
 
30
- def self.generate_datetime_column_description(column_name, column_type, klass_name)
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, column_type, klass_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, column_type, klass_name, klass)
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, column_type, klass_name, klass)
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, column_type, klass_name, klass)
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
- 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
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
- broken_cell_para
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 = ['encrypted_password', 'current_sign_in_ip', 'remote_address', 'last_sign_in_ip']
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 = get_current_adapter)
4
- if adapter == "postgresql"
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.get_current_adapter
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
- def self.read_columns_comment(table_name)
6
- raise NotImplementedError
7
- end
5
+ class DatabaseDocumenter::DatabaseComment::BaseDatabaseComment
6
+ def self.read_columns_comment(_table_name)
7
+ raise NotImplementedError
8
+ end
8
9
 
9
- def self.read_table_comment(table_name)
10
- raise NotImplementedError
11
- end
10
+ def self.read_table_comment(_table_name)
11
+ raise NotImplementedError
12
+ end
12
13
 
13
- def self.database_name
14
- Rails.application.config.database_configuration[Rails.env]['database'].freeze
15
- end
14
+ def self.database_name
15
+ Rails.application.config.database_configuration[Rails.env]['database'].freeze
16
16
  end
17
+ end
@@ -1,8 +1,6 @@
1
1
  module DatabaseDocumenter
2
2
  class DatabaseComment::MysqlDatabaseComment < DatabaseComment::BaseDatabaseComment
3
-
4
3
  def self.read_columns_comment(table_name)
5
-
6
4
  select_comment = <<-SQL
7
5
  SELECT `column_name`, `column_comment`
8
6
  FROM `information_schema`.`COLUMNS`
@@ -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
- 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';
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
- self.send("process_#{configuration['adapter']}_sql", tables_sql)
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DatabaseDocumenter
2
- VERSION = "0.1.6"
4
+ VERSION = '0.1.7'
3
5
  end
@@ -2,95 +2,5 @@ require "bundler/setup"
2
2
  require "database_documenter"
3
3
 
4
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
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( File.dirname(__FILE__), 'templates/database_documenter.rb').freeze
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.6
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 00:00:00.000000000 Z
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