dbdoc 0.1.0 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35a675eef6484bf4c2c6bb78521c9f322ff057804feec72e34061eb899153352
4
- data.tar.gz: 5e80744c319d0e2ea77b321704e317467e871e9d93a53ad77871c655106b8f3b
3
+ metadata.gz: c25899d4d18291f22be12f731230d64669f353e422f63d069791bd030ba3d9a8
4
+ data.tar.gz: be30d30193572b1b4dcf5141c010e9593e373d90c099f5e41ab40f5a06cf7e40
5
5
  SHA512:
6
- metadata.gz: b49a2edb0d2e31280b4bd8ed29ef109da6ff38fd6669df30a260f049ad5ad841bea5425d3c68fd9862f8e74116f4cbcb90e767f69e44aeed2c0910429ba13ac1
7
- data.tar.gz: 18b09c76bc60de2ffb1a574a9695d03b1be0ba5c40b3bedbeb2d59eaefbd4b431b4a370ea52cb46defb34a388b9a177e2dd6c4496e7a78bdf1c3d7ed0d607a82
6
+ metadata.gz: ecc79a5cabb4a5cea63e5ae92c9b2c5b331c539590da6464aef4a789c96167a50a9aa876a840186b8e4ac3d29c5db4db6024b0bc5c2183c02bf44037c89cf09b
7
+ data.tar.gz: d7b19650e610c9a2b0c4b7c26250571f3811f459016740b85925d6e82d56d8c72b7119a2b0c6c14237638b295d832156e7a4ab7784da9748670e290a96a33618
@@ -0,0 +1,2 @@
1
+ github: makaroni4
2
+ ko_fi: makaroni4
@@ -1,2 +1,27 @@
1
1
  Style/StringLiterals:
2
2
  EnforcedStyle: double_quotes
3
+
4
+ Metrics/ClassLength:
5
+ Max: 200
6
+
7
+ Metrics/MethodLength:
8
+ Max: 50
9
+
10
+ Style/Documentation:
11
+ Exclude:
12
+ - lib/confluence/api.rb
13
+ - lib/dbdoc/cli.rb
14
+
15
+ Metrics/AbcSize:
16
+ Exclude:
17
+ - lib/dbdoc/cli.rb
18
+
19
+ Layout/FirstHashElementIndentation:
20
+ EnforcedStyle: consistent
21
+
22
+ Metrics/BlockLength:
23
+ Exclude:
24
+ - spec/**/*_spec.rb
25
+
26
+ Security/YAMLLoad:
27
+ Enabled: false
data/Gemfile CHANGED
@@ -1,13 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  gemspec
4
6
 
5
- gem "httparty"
6
-
7
7
  group :development do
8
8
  gem "byebug"
9
9
  gem "coveralls", require: false
10
- gem "rubocop"
11
10
  gem "rake", "~> 12.0"
12
11
  gem "rspec", "~> 3.0"
12
+ gem "rubocop"
13
13
  end
@@ -1,7 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dbdoc (0.1.0)
4
+ dbdoc (0.3.5)
5
+ httparty (~> 0.18)
6
+ kramdown (~> 2.3)
5
7
 
6
8
  GEM
7
9
  remote: https://rubygems.org/
@@ -20,6 +22,8 @@ GEM
20
22
  mime-types (~> 3.0)
21
23
  multi_xml (>= 0.5.2)
22
24
  json (2.3.1)
25
+ kramdown (2.3.0)
26
+ rexml
23
27
  mime-types (3.3.1)
24
28
  mime-types-data (~> 3.2015)
25
29
  mime-types-data (3.2020.0512)
@@ -76,7 +80,6 @@ DEPENDENCIES
76
80
  byebug
77
81
  coveralls
78
82
  dbdoc!
79
- httparty
80
83
  rake (~> 12.0)
81
84
  rspec (~> 3.0)
82
85
  rubocop
data/README.md CHANGED
@@ -40,7 +40,11 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
40
40
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
41
41
 
42
42
  ```
43
- gem uninstall dbdoc && gem build dbdoc.gemspec && bundle && rake build && rake install && dbdoc help
43
+ Y | gem uninstall dbdoc && gem build dbdoc.gemspec && bundle && rake build && rake install && dbdoc help
44
+ ```
45
+
46
+ ```
47
+ Y | gem uninstall dbdoc && gem build dbdoc.gemspec && bundle && rake build && rake release
44
48
  ```
45
49
 
46
50
  ## Contributing
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
  require "rubocop/rake_task"
data/bin/dbdoc CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #!/usr/bin/env ruby
2
4
 
3
5
  require "dbdoc"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "lib/dbdoc/version"
2
4
 
3
5
  Gem::Specification.new do |spec|
@@ -22,4 +24,7 @@ Gem::Specification.new do |spec|
22
24
 
23
25
  spec.executables = ["dbdoc"]
24
26
  spec.require_paths = ["lib"]
27
+
28
+ spec.add_runtime_dependency "httparty", "~> 0.18"
29
+ spec.add_runtime_dependency "kramdown", "~> 2.3"
25
30
  end
@@ -1,4 +1,4 @@
1
- ||Name||Type||Description||
1
+ ||Name||Type||Foreign Key||Description||
2
2
  <%- columns.each do |column| -%>
3
- |<%= column[:name] %>|<%= column[:type] %>|<%= column[:description].strip %>|
3
+ |<%= column[:name] %>|<%= column[:type] %>|<%= column[:foreign_key] || " " %>|<%= column[:description].strip %>|
4
4
  <%- end -%>
@@ -3,6 +3,7 @@
3
3
  - :name: <%= name %>
4
4
  :type: <%= type %>
5
5
  :position: <%= position %>
6
+ :foreign_key: TODO
6
7
  :description: |
7
8
  TODO
8
9
  <%- end -%>
@@ -1 +1,5 @@
1
- Write table description here
1
+ This is a table description in a normal Markdown.
2
+
3
+ This is **bold**.
4
+
5
+ This is *italic*.
@@ -1,10 +1,8 @@
1
- h3. Count all users
1
+ ### How to count all users
2
2
 
3
3
  Use this query to count all user records:
4
4
 
5
- (!) This is a warning note.
6
-
7
- {code:language=sql}
5
+ ~~~sql
8
6
  SELECT COUNT(*)
9
7
  FROM users
10
- {code}
8
+ ~~~
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "httparty"
2
4
  require "json"
3
5
  require "yaml"
@@ -18,22 +20,25 @@ module Confluence
18
20
  end
19
21
 
20
22
  def delete_page(page_id:)
21
- HTTParty.delete(
23
+ response = HTTParty.delete(
22
24
  "https://dbdoc.atlassian.net/wiki/rest/api/content/#{page_id}", {
23
25
  headers: {
24
26
  "Authorization" => "Basic #{basic_auth}",
25
- "Content-Type" => "application/json"
27
+ "Content-Type" => "application/json"
26
28
  }
27
29
  }
28
30
  )
31
+
32
+ response.code == 200
29
33
  end
30
34
 
31
35
  def existing_pages
36
+ # TODO: paginate over all pages in the space
32
37
  response = HTTParty.get(
33
38
  "https://dbdoc.atlassian.net/wiki/rest/api/content/?&spaceKey=#{@space}", {
34
39
  headers: {
35
40
  "Authorization" => "Basic #{basic_auth}",
36
- "Content-Type" => "application/json"
41
+ "Content-Type" => "application/json"
37
42
  }
38
43
  }
39
44
  )
@@ -64,7 +69,7 @@ module Confluence
64
69
  "https://dbdoc.atlassian.net/wiki/rest/api/content/#{page_id}", {
65
70
  headers: {
66
71
  "Authorization" => "Basic #{basic_auth}",
67
- "Content-Type" => "application/json"
72
+ "Content-Type" => "application/json"
68
73
  },
69
74
  body: payload.to_json
70
75
  }
@@ -112,7 +117,7 @@ module Confluence
112
117
  "https://dbdoc.atlassian.net/wiki/rest/api/content/", {
113
118
  headers: {
114
119
  "Authorization" => "Basic #{basic_auth}",
115
- "Content-Type" => "application/json"
120
+ "Content-Type" => "application/json"
116
121
  },
117
122
  body: payload.to_json
118
123
  }
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kramdown"
4
+
5
+ module Confluence
6
+ # By default, all documentation should be written in a normal
7
+ # Markdown format. Later, when we're generating/uploading final documentation,
8
+ # we'll use Confluence::MarkdownConverter to convert normal Markdown
9
+ # into Confluence specific Markdown
10
+ # https://confluence.atlassian.com/bitbucketserver/markdown-syntax-guide-776639995.html
11
+ class MarkdownConverter
12
+ CONVERTERS = %w[
13
+ convert_h1
14
+ convert_h2
15
+ convert_h3
16
+ convert_h4
17
+ convert_h5
18
+ convert_bold
19
+ convert_italic
20
+ convert_code
21
+ convert_unordered_list
22
+ ].freeze
23
+
24
+ def convert(original_markdown)
25
+ return if original_markdown.nil?
26
+
27
+ output = original_markdown
28
+
29
+ CONVERTERS.each do |converter_method|
30
+ output = send(converter_method, output)
31
+ end
32
+
33
+ output
34
+ end
35
+
36
+ private
37
+
38
+ def convert_h1(markdown)
39
+ markdown.gsub(/^(#)([^#].+)$/, 'h1.\2')
40
+ end
41
+
42
+ def convert_h2(markdown)
43
+ markdown.gsub(/^(##)([^\#].+)$/, 'h2.\2')
44
+ end
45
+
46
+ def convert_h3(markdown)
47
+ markdown.gsub(/^(###)([^\#].+)$/, 'h3.\2')
48
+ end
49
+
50
+ def convert_h4(markdown)
51
+ markdown.gsub(/^(####)([^\#].+)$/, 'h4.\2')
52
+ end
53
+
54
+ def convert_h5(markdown)
55
+ markdown.gsub(/^(#####)([^\#].+)$/, 'h5.\2')
56
+ end
57
+
58
+ def convert_bold(markdown)
59
+ markdown.gsub(/(\*\*)(\w+)(\*\*)/, '*\2*')
60
+ end
61
+
62
+ def convert_italic(markdown)
63
+ markdown.gsub(/(\*)(\w+)(\*)/, '_\2_')
64
+ end
65
+
66
+ def convert_code(markdown)
67
+ output_lines = []
68
+
69
+ markdown.each_line do |line|
70
+ if line =~ /^\~{3}\w+$/
71
+ language = line.gsub("~~~", "").strip
72
+
73
+ output_lines.push("{code:language=#{language}}\n")
74
+ elsif line =~ /^\~{3}$/
75
+ output_lines.push("{code}")
76
+ else
77
+ output_lines.push(line)
78
+ end
79
+ end
80
+
81
+ output_lines.join
82
+ end
83
+
84
+ def convert_unordered_list(markdown)
85
+ markdown.gsub(/^\*\s(.+)$/, '- \1')
86
+ end
87
+
88
+ # TODO: add nested list convertion
89
+ # TODO: add numbered list convertion
90
+
91
+ # TODO: add emoji
92
+ # https://support.atlassian.com/confluence-cloud/docs/use-symbols-emojis-and-special-characters/
93
+ end
94
+ end
@@ -1,10 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "dbdoc/constants"
4
+ require_relative "../confluence/markdown_converter"
2
5
  require_relative "../confluence/api"
3
6
 
4
- module Dbdoc
7
+ module Confluence
8
+ # Confluence::Uploader class knows how to uploaded
9
+ # documentation to the Confluence Space provided in the config.yml.
10
+ #
11
+ # Uploader creates a root page with the database name from config.yml
12
+ # and creates nested pages for schemas and tables.
13
+ #
14
+ # A Confluence ID of each page created by dbdoc
15
+ # is logged to pages.yml file stored in the user's folder
16
+ # with the documentation.
5
17
  class Uploader
6
- def initialize(config: {})
7
- @config = config
18
+ def initialize(local_path: Dir.pwd)
19
+ @config = Dbdoc::Config.new(local_path: local_path).load
8
20
  @confluence_api = Confluence::Api.new
9
21
  @doc_folder = File.join(Dir.pwd, "doc")
10
22
  end
@@ -14,24 +26,45 @@ module Dbdoc
14
26
  delete_pages_for_dropped_schemas_or_tables
15
27
  end
16
28
 
29
+ def space_pages
30
+ @confluence_api.existing_pages["results"]
31
+ end
32
+
33
+ def print_space_pages
34
+ space_pages.each do |page|
35
+ page_title = page["title"]
36
+ page_id = page["id"]
37
+
38
+ puts "#{page_title}: #{page_id}"
39
+ end
40
+ end
41
+
17
42
  def clear_confluence_space
18
- # TODO paginate and fetch all Confluence pages
19
- # TODO ask user to Yn if they want to proceed with deletion
20
- # TODO iterate over each page_id, unlog it from page_ids.yml and @confluence_api.delete_page(page_id:)
43
+ YAML.load(File.read(page_ids_file))
44
+
45
+ space_pages.each do |page|
46
+ page_key = page["title"]
47
+ page_id = page["id"]
48
+
49
+ puts "--> Deleting #{page_key} #{page_id}"
50
+
51
+ unlog_page_id(key: page_key) if @confluence_api.delete_page(page_id: page_id)
52
+ end
21
53
  end
22
54
 
23
55
  private
24
56
 
57
+ # rubocop:disable Metrics/AbcSize
25
58
  def delete_pages_for_dropped_schemas_or_tables
26
59
  uploaded_pages = YAML.load(File.read(page_ids_file))
27
60
 
28
- uploaded_pages.each do |key, params|
61
+ uploaded_pages.each do |key, _params|
29
62
  next if key == "root"
30
63
 
31
64
  if key.start_with?("schema:")
32
65
  schema_name = key.gsub("schema:", "")
33
66
 
34
- unless Dir.exists?(File.join(@doc_folder, schema_name))
67
+ unless Dir.exist?(File.join(@doc_folder, schema_name))
35
68
  page_id = uploaded_pages[key][:page_id]
36
69
  puts "--> delete page #{key}: #{page_id}"
37
70
  @confluence_api.delete_page(page_id: page_id)
@@ -40,7 +73,7 @@ module Dbdoc
40
73
  elsif key.start_with?("table:")
41
74
  schema_name, table_name = key.gsub("table:", "").split(".")
42
75
 
43
- unless Dir.exists?(File.join(@doc_folder, schema_name, table_name))
76
+ unless Dir.exist?(File.join(@doc_folder, schema_name, table_name))
44
77
  page_id = uploaded_pages[key][:page_id]
45
78
  puts "--> delete page #{key}: #{page_id}"
46
79
  @confluence_api.delete_page(page_id: page_id)
@@ -49,6 +82,7 @@ module Dbdoc
49
82
  end
50
83
  end
51
84
  end
85
+ # rubocop:enable Metrics/AbcSize
52
86
 
53
87
  def create_or_updates_pages
54
88
  root_page_id = create_root_db_page[:page_id]
@@ -71,7 +105,7 @@ module Dbdoc
71
105
  def page_ids_file
72
106
  file = File.join(Dir.pwd, "page_ids.yml")
73
107
 
74
- unless File.exists?(file)
108
+ unless File.exist?(file)
75
109
  File.open(file, "w") do |f|
76
110
  f.puts("--- {}")
77
111
  end
@@ -97,7 +131,7 @@ module Dbdoc
97
131
  version: 0
98
132
  }
99
133
 
100
- if page_ids[key][:version] == 0
134
+ if page_ids.dig(key, :version).zero?
101
135
  puts "--> create page #{key}: #{page_id}"
102
136
  else
103
137
  puts "--> update page #{key}: #{page_id}"
@@ -123,9 +157,11 @@ module Dbdoc
123
157
  def create_root_db_page
124
158
  page_id = latest_page_id(key: "root")
125
159
 
126
- return {
127
- page_id: page_id
128
- } if page_id
160
+ if page_id
161
+ return {
162
+ page_id: page_id
163
+ }
164
+ end
129
165
 
130
166
  db_name = @config["db"]["name"]
131
167
  @confluence_api.create_page(
@@ -162,37 +198,50 @@ module Dbdoc
162
198
  end
163
199
  end
164
200
 
201
+ def markdown(input)
202
+ Confluence::MarkdownConverter.new.convert(input)
203
+ end
204
+
205
+ # rubocop:disable Metrics/AbcSize
165
206
  def upload_table(schema_name:, table_name:, schema_page_id:)
166
207
  table_folder = File.join(@doc_folder, schema_name, table_name)
167
208
 
168
- table_description = File.read(File.join(table_folder, "description.md"))
209
+ table_description = markdown(File.read(File.join(table_folder, "description.md")))
169
210
 
170
211
  examples_folder = File.join(table_folder, "examples")
171
- table_examples = Dir[File.join(examples_folder, "*.md")].map { |f| File.read(f) }
212
+ table_examples = Dir[File.join(examples_folder, "*.md")].map do |f|
213
+ markdown(File.read(f))
214
+ end
172
215
 
173
- columns_markdown_template_file = File.join(DBDOC_HOME, "doc_files", "columns.md.erb")
216
+ columns_markdown_template_file = File.join(Dbdoc::DBDOC_HOME, "doc_files", "columns.md.erb")
174
217
 
175
218
  columns_table_template = ERB.new(
176
219
  File.read(columns_markdown_template_file),
177
220
  nil,
178
221
  "-"
179
222
  )
223
+
224
+ columns_doc = YAML.load(File.read(File.join(table_folder, "columns.yml")))
225
+ columns_doc.each do |col|
226
+ col[:description] = markdown(col[:description])
227
+ end
228
+
180
229
  columns_table = columns_table_template.result_with_hash({
181
- columns: YAML.load(File.read(File.join(table_folder, "columns.yml")))
230
+ columns: columns_doc
182
231
  })
183
232
 
184
- page_body = <<-MARKDOWN
185
- h2. Description
233
+ page_body = <<~MARKDOWN
234
+ h2. Description
186
235
 
187
- #{table_description}
236
+ #{table_description}
188
237
 
189
- h2. Columns
238
+ h2. Columns
190
239
 
191
- #{columns_table}
240
+ #{columns_table}
192
241
 
193
- h2. Examples
242
+ h2. Examples
194
243
 
195
- #{table_examples.join("\n") }
244
+ #{table_examples.join("\n")}
196
245
  MARKDOWN
197
246
 
198
247
  page_title = schema_name == "public" ? table_name : "#{schema_name}.#{table_name}"
@@ -201,7 +250,7 @@ h2. Examples
201
250
  page_id = latest_page_id(key: page_key)
202
251
 
203
252
  if page_id
204
- response = @confluence_api.update_page(
253
+ @confluence_api.update_page(
205
254
  page_id: page_id,
206
255
  page_title: page_title,
207
256
  body: page_body,
@@ -221,5 +270,6 @@ h2. Examples
221
270
  log_page_id(key: "table:#{schema_name}.#{table_name}", page_id: table_page_id)
222
271
  end
223
272
  end
273
+ # rubocop:enable Metrics/AbcSize
224
274
  end
225
275
  end
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "dbdoc/constants"
2
4
  require "dbdoc/version"
3
5
  require "dbdoc/config"
4
6
  require "dbdoc/manager"
5
- require "dbdoc/uploader"
7
+ require "dbdoc/folder_initializer"
8
+ require "confluence/uploader"
6
9
 
7
10
  module Dbdoc
8
11
  class Error < StandardError; end
@@ -1,130 +1,116 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "optparse"
2
4
 
3
5
  $LOAD_PATH << File.expand_path(__dir__)
4
6
 
5
7
  module Dbdoc
6
8
  class CLI
9
+ COMMANDS = {
10
+ install: %(
11
+ Generates all necessary config files and documentation folders.
12
+
13
+ Run this command in an empty directory.
14
+ ),
15
+ query: %(
16
+ Prints a query that you need to run in the database \
17
+ you're going to document.
18
+
19
+ Export the result of this query to the "schema.csv" file \
20
+ and copy it over to the "schema" folder for processing.
21
+ ),
22
+ plan: %(
23
+ Shows you what columns/tables/schemas are new and \
24
+ going to be added/deleted from the documentation.
25
+ ),
26
+ apply: %(
27
+ Generates boilerplate documentation for newly added \
28
+ columns/tables/schemas.
29
+
30
+ Drops documentation for columns/tables/schemas that \
31
+ were deleted from the database.
32
+ ),
33
+ "confluence:upload": %(
34
+ Uploads current documentation to Confluence: pages for \
35
+ new tables/schemas will be added, pages for dropped tables/schemas \
36
+ will be deleted from your Confluence space.
37
+ ),
38
+ "confluece:pages": %(
39
+ Lists all pages in your dbdoc Confluence space \
40
+ (created manually or via dbdoc).
41
+ ),
42
+ "confluence:clear": %(
43
+ IMPORTANT This command will delete ALL pages from \
44
+ the Confluence space (pages created via dbdoc AND pages that were added manually).
45
+ ),
46
+ todo: %(
47
+ Shows you the documentation that needs to be written.
48
+ )
49
+ }.freeze
50
+
51
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
7
52
  def run(args = [])
8
- if args.first == "init"
9
- require "fileutils"
10
-
11
- schema_folder = File.join(Dir.pwd, "schema")
12
- unless Dir.exists?(schema_folder)
13
- Dir.mkdir(schema_folder)
14
- end
15
-
16
- doc_folder = File.join(Dir.pwd, "doc")
17
- unless Dir.exists?(doc_folder)
18
- Dir.mkdir(doc_folder)
19
- end
20
-
21
- target_file = File.join(Dir.pwd, "config.yml")
22
- config_file = File.join(File.expand_path(__dir__), "../..", "config", "default.yml")
23
-
24
- FileUtils.cp(config_file, target_file) unless File.exists?(target_file)
25
-
26
- target_file = File.join(Dir.pwd, ".gitignore")
27
- config_file = File.join(File.expand_path(__dir__), "../..", "config", "gitignore.template")
28
-
29
- FileUtils.cp(config_file, target_file) unless File.exists?(target_file)
30
-
31
- target_file = File.join(Dir.pwd, "confluence.yml")
32
- config_file = File.join(File.expand_path(__dir__), "../..", "config", "confluence.yml")
33
-
34
- FileUtils.cp(config_file, target_file) unless File.exists?(target_file)
35
-
36
- 0
53
+ if args.first == "install"
54
+ Dbdoc::FolderInitializer.new.init
37
55
  elsif args.first == "query"
38
- options = extract_options(args)
39
-
40
- config = Dbdoc::Config.load
41
- config.merge!(options)
42
-
43
- db_type = config["db"]["type"]
44
- query_file = File.join(File.expand_path(__dir__), "../..", "config", "schema_queries", "#{db_type}.sql")
45
- query = File.read(query_file)
46
-
47
- puts query
48
-
49
- 0
56
+ puts manager.query
50
57
  elsif args.first == "plan"
51
- options = extract_options(args)
58
+ plan = manager.plan
52
59
 
53
- config = Dbdoc::Config.load
54
- config.merge!(options)
55
-
56
- manager = Dbdoc::Manager.new(config: config)
57
- manager.plan
60
+ puts "--> New columns:"
61
+ plan[:new_columns].each do |column|
62
+ puts column
63
+ end
58
64
 
59
- 0
65
+ puts "--> Columns to drop:"
66
+ plan[:columns_to_drop].each do |column|
67
+ puts column
68
+ end
60
69
  elsif args.first == "apply"
61
- options = extract_options(args)
62
-
63
- config = Dbdoc::Config.load
64
- config.merge!(options)
65
-
66
- manager = Dbdoc::Manager.new(config: config)
67
70
  manager.apply
68
-
69
- 0
70
- elsif args.first == "upload"
71
- options = extract_options(args)
72
-
73
- config = Dbdoc::Config.load
74
- config.merge!(options)
75
-
76
- uploader = Dbdoc::Uploader.new(config: config)
71
+ elsif args.first == "confluence:upload"
77
72
  uploader.upload
78
-
79
- 0
80
- elsif args.first == "clear_confluence_space"
81
- options = extract_options(args)
82
-
83
- config = Dbdoc::Config.load
84
- config.merge!(options)
85
-
86
- uploader = Dbdoc::Uploader.new(config: config)
73
+ elsif args.first == "confluence:pages"
74
+ uploader.print_space_pages
75
+ elsif args.first == "confluence:clear"
87
76
  uploader.clear_confluence_space
88
-
89
- 0
77
+ elsif args.first == "todo"
78
+ manager.todo
90
79
  elsif args.first == "help"
91
- puts "--> SOME HELP"
92
-
93
- 0
80
+ print_help
81
+ elsif args.first == "version"
82
+ puts Dbdoc::VERSION
94
83
  end
95
84
 
96
85
  0
97
86
  end
87
+ # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
98
88
 
99
89
  private
100
90
 
101
- # This method is needed to unindent
102
- # ["here document"](https://en.wikibooks.org/wiki/Ruby_Programming/Here_documents)
103
- # help description.
104
- #
105
91
  def unindent(str)
106
92
  str.gsub(/^#{str.scan(/^[ \t]+(?=\S)/).min}/, "")
107
93
  end
108
94
 
109
- def extract_options(args)
110
- options = {}
111
-
112
- OptionParser.new do |opts|
113
- opts.banner = unindent(<<-TEXT)
114
- dbdoc help
115
-
116
- 1. dbdoc query
95
+ def manager
96
+ @manager ||= Dbdoc::Manager.new
97
+ end
117
98
 
118
- This will print you a query you need to run to export your database schema.
119
- TEXT
99
+ def uploader
100
+ @uploader ||= Confluence::Uploader.new
101
+ end
120
102
 
121
- opts.on("-v", "--version", "Prints current version of dbdoc") do
122
- puts Dbdoc::VERSION
123
- exit 0
124
- end
125
- end.parse!(args)
103
+ def print_help
104
+ puts unindent <<-TEXT
105
+ Usage: dbdoc [command]
106
+ TEXT
107
+ puts
126
108
 
127
- options
109
+ COMMANDS.each do |command, description|
110
+ puts "dbdoc #{command}"
111
+ puts
112
+ puts unindent(description)
113
+ end
128
114
  end
129
115
  end
130
116
  end
@@ -1,27 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "yaml"
2
4
  require "dbdoc/constants"
3
5
 
4
6
  module Dbdoc
7
+ # Dbdoc::Config class knows how to load the default config from the
8
+ # dbdoc gem folder. Later, if needed, this class could be used to merge
9
+ # user-defined config with the default one.
5
10
  class Config
6
- FILE_NAME = "config.yml".freeze
7
- DEFAULT_FILE = File.join(DBDOC_HOME, "config", "default.yml")
8
-
9
- class << self
10
- # Be default gem will try to load config file in user's project folder.
11
- # Then user's config (or empty object) will be merge with the default config
12
- # from gem's folder.
13
- #
14
- def load
15
- user_config = File.exist?(user_file) ? YAML.safe_load(File.read(user_file)) : {}
16
- default_config = YAML.safe_load(File.read(DEFAULT_FILE))
17
-
18
- default_config.merge(user_config)
19
- end
11
+ def initialize(local_path: Dir.pwd)
12
+ @local_path = local_path
13
+ end
20
14
 
21
- private
15
+ def load
16
+ local_config_file = File.join(@local_path, "config.yml")
22
17
 
23
- def user_file
24
- File.join(Dir.pwd, FILE_NAME)
18
+ if File.exist?(local_config_file)
19
+ YAML.load(File.read(local_config_file))
20
+ else
21
+ {}
25
22
  end
26
23
  end
27
24
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dbdoc
2
4
  DBDOC_HOME = File.realpath(File.join(__dir__, "..", ".."))
3
5
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "fileutils"
5
+ require "dbdoc/constants"
6
+
7
+ module Dbdoc
8
+ # Dbdoc::FolderInitializer class knows how to turn an emtpy
9
+ # folder into a dbdoc folder.
10
+ #
11
+ class FolderInitializer
12
+ def initialize(local_path: Dir.pwd)
13
+ @config = Dbdoc::Config.new(local_path: local_path).load
14
+ end
15
+
16
+ def init
17
+ create_schema_folder
18
+ create_doc_folder
19
+ create_config_file
20
+ create_gitignore_file
21
+ create_confluence_config_file
22
+ end
23
+
24
+ private
25
+
26
+ # schema folder is the one user copies the schema.csv to
27
+ # before updating/generating documentation
28
+ def create_schema_folder
29
+ schema_folder = File.join(Dir.pwd, "schema")
30
+ Dir.mkdir(schema_folder) unless Dir.exist?(schema_folder)
31
+ end
32
+
33
+ # doc folder stores all the database documentation files
34
+ def create_doc_folder
35
+ doc_folder = File.join(Dir.pwd, "doc")
36
+ Dir.mkdir(doc_folder) unless Dir.exist?(doc_folder)
37
+ end
38
+
39
+ def create_file(default_file_name, target_file_name)
40
+ target_file = File.join(Dir.pwd, target_file_name)
41
+
42
+ return if File.exist?(target_file)
43
+
44
+ config_file = File.join(File.expand_path(__dir__), "../..", "config", default_file_name)
45
+
46
+ FileUtils.cp(config_file, target_file)
47
+ end
48
+
49
+ def create_config_file
50
+ create_file("default.yml", "config.yml")
51
+ end
52
+
53
+ def create_gitignore_file
54
+ create_file("gitignore.template", ".gitignore")
55
+ end
56
+
57
+ def create_confluence_config_file
58
+ create_file("confluence.yml", "confluence.yml")
59
+ end
60
+ end
61
+ end
@@ -1,30 +1,58 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "yaml"
4
+ require "erb"
5
+ require "fileutils"
2
6
  require "dbdoc/constants"
3
7
 
4
8
  module Dbdoc
9
+ # Dbdoc::Manager class manages database schema documentation.
10
+ #
11
+ # It knows how to generate and update documentation based on the database
12
+ # schema generated by the schema query for user's database.
5
13
  class Manager
6
- def initialize(config: {})
7
- @config = Dbdoc::Config.load.merge(config)
14
+ def initialize(local_path: Dir.pwd)
15
+ @local_path = local_path
16
+ @config = Dbdoc::Config.new(local_path: local_path).load
8
17
  end
9
18
 
10
- def plan(verbose: true)
11
- puts "--> PLAN"
12
- puts
13
- puts
14
-
15
- input_schema = read_input_schema.map { |r| r.first(4) }.map { |r| r.join(":") }
19
+ def plan
20
+ input_schema = read_input_schema.map { |r| r.first(4).join(":") }
16
21
  current_schema = read_documented_schema
17
22
 
18
- puts "--> New columns:"
19
- pp input_schema - current_schema
20
- puts
21
- puts
23
+ {
24
+ new_columns: input_schema - current_schema,
25
+ columns_to_drop: current_schema - input_schema
26
+ }
27
+ end
28
+
29
+ def todo
30
+ doc_folder_files = File.join(@local_path, "doc", "**/*")
31
+
32
+ Dir[doc_folder_files].each do |file|
33
+ next if file == "."
34
+ next if file == ".."
35
+ next if File.directory?(file)
22
36
 
23
- puts "--> Columns to drop:"
24
- pp current_schema - input_schema
37
+ File.read(file).split("\n").each_with_index do |line, i|
38
+ next unless line.include?("TODO")
39
+
40
+ relative_path = file.gsub(@local_path, "")
41
+
42
+ puts "#{relative_path}:#{i + 1}"
43
+ end
44
+ end
45
+ end
46
+
47
+ def query
48
+ db_type = @config["db"]["type"]
49
+ query_file = File.join(File.expand_path(__dir__), "../..", "config", "schema_queries", "#{db_type}.sql")
50
+
51
+ File.read(query_file)
25
52
  end
26
53
 
27
- def apply(path: Dir.pwd, verbose: true)
54
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
55
+ def apply
28
56
  puts "--> APPLY"
29
57
  puts
30
58
  puts
@@ -35,14 +63,16 @@ module Dbdoc
35
63
  added_columns = input_schema - current_schema
36
64
  dropped_columns = current_schema - input_schema
37
65
 
38
- doc_folder = File.join(Dir.pwd, "doc")
66
+ doc_folder = File.join(@local_path, "doc")
67
+
68
+ Dir.mkdir(doc_folder) unless Dir.exist?(doc_folder)
39
69
 
40
70
  ## DROP COLUMNS
41
71
  dropped_columns.each do |column|
42
- schema_name, table_name, column_name, column_type = column.split(":")
72
+ schema_name, table_name, column_name = column.split(":")
43
73
 
44
74
  columns_file = File.join(doc_folder, schema_name, table_name, "columns.yml")
45
- next unless File.exists?(columns_file)
75
+ next unless File.exist?(columns_file)
46
76
 
47
77
  columns = YAML.load(File.read(columns_file))
48
78
  columns.reject! { |c| c[:name] == column_name }
@@ -69,7 +99,7 @@ module Dbdoc
69
99
  next unless File.directory?(table_folder)
70
100
 
71
101
  columns_file = File.join(table_folder, "columns.yml")
72
- next unless File.exists?(columns_file)
102
+ next unless File.exist?(columns_file)
73
103
 
74
104
  columns = YAML.load(File.read(columns_file))
75
105
 
@@ -93,16 +123,18 @@ module Dbdoc
93
123
 
94
124
  create_new_columns(added_columns)
95
125
  end
126
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
96
127
 
97
128
  private
98
129
 
99
130
  def input_schema
100
- File.read(File.join(Dir.pwd, "schema", "schema.csv"))
131
+ File.read(File.join(@local_path, "schema", "schema.csv"))
101
132
  end
102
133
 
134
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
103
135
  def read_input_schema
104
136
  rows = input_schema.split("\n")
105
- with_header = rows[0].include?("schema_name")
137
+ with_header = rows[0].include?("table_schema")
106
138
 
107
139
  rows.shift if with_header
108
140
 
@@ -110,38 +142,37 @@ module Dbdoc
110
142
  r.split(",").map(&:strip).map { |c| c.gsub('"', "") }.first(5)
111
143
  end
112
144
 
113
- config = YAML.load(File.read("config.yml"))
114
-
115
- if @config["ignorelist"]
116
- @config["ignorelist"].map { |r| r.split(/[\.\#]/) }.each do |b|
117
- schema_pattern, table_pattern, column_pattern = b
145
+ @config["ignorelist"]&.map { |r| r.split(/[\.\#]/) }&.each do |b|
146
+ schema_pattern, table_pattern, column_pattern = b
118
147
 
119
- rows.reject! do |row|
120
- schema_name, table_name, column_name, _, _ = row
148
+ rows.reject! do |row|
149
+ schema_name, table_name, column_name, = row
121
150
 
122
- if column_pattern
123
- next unless column_name =~ Regexp.new(column_pattern.gsub("*", ".*"))
124
- end
125
-
126
- if table_pattern
127
- next unless table_name =~ Regexp.new(table_pattern.gsub("*", ".*"))
128
- end
151
+ if column_pattern
152
+ next unless column_name =~ Regexp.new(column_pattern.gsub("*", ".*"))
153
+ end
129
154
 
130
- if schema_pattern
131
- next unless schema_name =~ Regexp.new(schema_pattern.gsub("*", ".*"))
132
- end
155
+ if table_pattern
156
+ next unless table_name =~ Regexp.new(table_pattern.gsub("*", ".*"))
157
+ end
133
158
 
134
- true
159
+ if schema_pattern
160
+ next unless schema_name =~ Regexp.new(schema_pattern.gsub("*", ".*"))
135
161
  end
162
+
163
+ true
136
164
  end
137
165
  end
138
166
 
139
167
  rows
140
168
  end
169
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
141
170
 
171
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
142
172
  def read_documented_schema
143
- doc_folder = File.join(Dir.pwd, "doc")
173
+ doc_folder = File.join(@local_path, "doc")
144
174
 
175
+ return [] unless Dir.exist?(doc_folder)
145
176
  return [] if Dir.empty?(doc_folder)
146
177
 
147
178
  keys = []
@@ -160,9 +191,9 @@ module Dbdoc
160
191
  next unless File.directory?(table_folder)
161
192
 
162
193
  columns_file = File.join(table_folder, "columns.yml")
163
- next unless File.exists?(columns_file)
194
+ next unless File.exist?(columns_file)
164
195
 
165
- columns = YAML.load(File.read(columns_file))
196
+ columns = YAML.load(File.read(columns_file), [Symbol])
166
197
  columns.each do |column|
167
198
  keys.push([
168
199
  schema_name,
@@ -176,9 +207,11 @@ module Dbdoc
176
207
 
177
208
  keys
178
209
  end
210
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
179
211
 
212
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize
180
213
  def create_new_columns(added_columns)
181
- doc_folder = File.join(Dir.pwd, "doc")
214
+ doc_folder = File.join(@local_path, "doc")
182
215
 
183
216
  added_columns.map! { |r| r.split(":") }
184
217
  new_columns = read_input_schema.select do |row|
@@ -224,17 +257,18 @@ module Dbdoc
224
257
  # 4. create table columns.yml
225
258
  columns_yaml = File.join(table_folder, "columns.yml")
226
259
 
227
- unless File.exists?(columns_yaml)
228
- columns_erb_tamplate_file = File.join(DBDOC_HOME, "doc_files", "columns.yml.erb")
229
- columns_yaml_template = ERB.new(File.read(columns_erb_tamplate_file), nil, "-")
230
- File.open(columns_yaml, "w") do |f|
231
- f.puts columns_yaml_template.result_with_hash({
232
- columns: columns
233
- })
234
- end
260
+ next if File.exist?(columns_yaml)
261
+
262
+ columns_erb_tamplate_file = File.join(DBDOC_HOME, "doc_files", "columns.yml.erb")
263
+ columns_yaml_template = ERB.new(File.read(columns_erb_tamplate_file), nil, "-")
264
+ File.open(columns_yaml, "w") do |f|
265
+ f.puts columns_yaml_template.result_with_hash({
266
+ columns: columns
267
+ })
235
268
  end
236
269
  end
237
270
  end
238
271
  end
272
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/AbcSize
239
273
  end
240
274
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dbdoc
2
- VERSION = "0.1.0".freeze
4
+ VERSION = "0.3.5"
3
5
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dbdoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anatoli Makarevich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-29 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-09-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.18'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.18'
27
+ - !ruby/object:Gem::Dependency
28
+ name: kramdown
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.3'
13
41
  description: Dbdoc is a tool to keep your database documentation up-to-date and version
14
42
  controlled.
15
43
  email:
@@ -19,6 +47,7 @@ executables:
19
47
  extensions: []
20
48
  extra_rdoc_files: []
21
49
  files:
50
+ - ".github/FUNDING.yml"
22
51
  - ".gitignore"
23
52
  - ".rspec"
24
53
  - ".rubocop.yml"
@@ -43,12 +72,14 @@ files:
43
72
  - doc_files/table_description.md
44
73
  - doc_files/table_example.md
45
74
  - lib/confluence/api.rb
75
+ - lib/confluence/markdown_converter.rb
76
+ - lib/confluence/uploader.rb
46
77
  - lib/dbdoc.rb
47
78
  - lib/dbdoc/cli.rb
48
79
  - lib/dbdoc/config.rb
49
80
  - lib/dbdoc/constants.rb
81
+ - lib/dbdoc/folder_initializer.rb
50
82
  - lib/dbdoc/manager.rb
51
- - lib/dbdoc/uploader.rb
52
83
  - lib/dbdoc/version.rb
53
84
  homepage: https://github.com/sqlhabit/dbdoc
54
85
  licenses: