RailsでMySQLのDBから定義書を自動生成する

ある時、DBの定義書を確認したいと思い生成に成功したため実現方法を紹介します。

# 生成までの大まかな流れ

1. mysqldumpでXML形式のダンプファイルを出力
1. ダンプしたXMLファイルをHTML形式のファイルに変換
1. HTML形式のファイルをPDFに変換

# 実装方法

XMLに変換するためのテンプレートファイルを用意します。

```xml:doc/db/schema/style/schema.xsl
<?xml version="1.0" encoding="utf8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:s="http://sqlfairy.sourceforge.net/sqlfairy.xml">
	<xsl:output method="html" encoding="utf8" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" />
	<xsl:template match="database">
		<html lang="ja">
			<head>
				<meta charset="utf-8" />
				<title>テーブル定義</title>
				<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
				<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css" />
				<style>
					html body {
					font-family: sans-serif !important;;
					}
					table.htable {
					margin: 3em auto 1em auto !important;
					}
					table.htable th {
					border-left: 10px solid #e5e5e5 !important;
					}
					footer {
					border-top: 1px solid #e5e5e5;
					padding: 0.5em;
					}
				</style>
			</head>
			<body>
				<div class="container">
					<h1 class="page-header">テーブル定義</h1>
					<xsl:apply-templates select="table_structure" /> </div>
				<footer class="text-center"> Created by showwin. </footer>
			</body>
		</html>
	</xsl:template>
	<xsl:template match="table_structure">
		<table class="table table-bordered htable">
			<tbody>
				<tr class="active">
					<th>テーブル名</th>
					<td>
						<xsl:value-of select="@name" />
					</td>
					<td>
						<xsl:apply-templates select="options" />
					</td>
				</tr>
			</tbody>
		</table>
		<table class="table table-condensed">
			<thead>
				<tr>
					<th class="text-right">#</th>
					<th>物理名</th>
					<th>論理名</th>
					<th>型</th>
					<th>NULL</th>
					<th>デフォルト値</th>
					<th>主キー</th>
					<th>ユニーク</th>
					<th>その他</th>
				</tr>
			</thead>
			<tbody>
				<xsl:apply-templates select="field" /> </tbody>
		</table>
	</xsl:template>
	<xsl:template match="field">
		<tr>
			<td class="text-right">
				<xsl:value-of select="position()" />
			</td>
			<td>
				<xsl:value-of select="@Field" />
			</td>
			<td>
				<xsl:value-of select="@Comment" />
			</td>
			<td>
				<xsl:value-of select="@Type" />
			</td>
			<td>
				<xsl:if test="@Null='YES'"><span class="glyphicon glyphicon-ok"></span></xsl:if>
			</td>
			<td>
				<xsl:value-of select="@Default" />
			</td>
			<td>
				<xsl:if test="@Key='PRI'"><span class="glyphicon glyphicon-ok"></span></xsl:if>
			</td>
			<td>
				<xsl:if test="@Key='UNI'"><span class="glyphicon glyphicon-ok"></span></xsl:if>
			</td>
			<td>
				<xsl:value-of select="@Extra" />
			</td>
		</tr>
	</xsl:template>
	<xsl:template match="options">
		<xsl:value-of select="@Comment" /> </xsl:template>
</xsl:stylesheet>
```

生成はrakeコマンドで実行できるようにします。

```ruby:lib/create_db_schema_document.rake
if Rails.env.development?
  namespace :db_schema_document do
    desc 'データベースの定義書をschemaディレクトリに作成'
    schema_path = './doc/db/schema'
    xml_schema_document = "#{schema_path}/schema_document.xml"
    convert_xml_to_html_option =
      "#{schema_path}/schema_document.html #{schema_path}/style/schema.xsl #{schema_path}/schema_document.xml"
    convert_html_to_pdf_option = "#{schema_path}/schema_document.html #{schema_path}/schema_document.pdf"
    task create: convert_html_to_pdf_option

    # HTMLをPDFに変換
    file convert_html_to_pdf_option => convert_xml_to_html_option do |task|
      `wkhtmltopdf #{task.name}`
    end
    # XMLをHTMLに変換
    file convert_xml_to_html_option => xml_schema_document do |task|
      `xsltproc -o #{task.name}`
    end
    # schemaの構造をXML形式で出力
    file xml_schema_document => %i[environment] do |task|
      config = ActiveRecord::Base.connection_db_config.configuration_hash
      options = {
        h: config[:host],
        P: config[:port],
        u: config[:username],
        p: config[:password],
      }.compact.map { |k, v| "-#{k} '#{v}'" }.join(' ')
      `mysqldump #{options} --no-data --xml '#{config[:database]}' > '#{task.name}'`
    end
  end
  Rake::Task['db:schema:dump'].enhance do
    Rake::Task['db_schema_document:create'].invoke
  end
end
```

# 実行方法

`xsltproc`と`wkhtmltopdf`が使える環境であることを事前に確認しておきます。

`bundle exec rails db:schema:dump`を実行するとデータベース定義書がschemaディレクトリ内に作成されます。

`bundle exec rails db:schema:dump`でなく個別で実行する場合は、`bundle exec rails db_schema_document:create`で生成することができます。