読者です 読者をやめる 読者になる 読者になる

DataMapperとMySQLの組み合わせでDDLにコメントを含める

Ruby

dm-core-1.2.0の話です。

DDLにコメントを含めたい!という要望があったので。

モンキーパッチ

# dm-core-1.2.0/lib/dm-core/property.rb
# commentを定義可能にする
module DataMapper
  class Property
    accept_options :comment
    attr_reader :comment
  end
end

# dm-migrations-1.2.0/lib/dm-migrations/adapters/dm-mysql-adapter.rb
# dm-mysql-adapter-1.2.0/lib/dm-mysql-adapter/adapter.rb
# create_table、propertyの定義部分にコメントを差し込む
# auto_migration、migrationのための対応
module DataMapper
  module Adapters
    class MysqlAdapter < DataObjectsAdapter
      module CommentExtensions
        def create_table_statement(connection, model, properties)
          if model.respond_to?(:table_comment)
            "#{super} COMMENT = '#{model.table_comment}'"
          else
            super
          end
        end
        def property_schema_hash(property)
          schema = super
          schema[:comment] = property.options[:comment]
          schema
        end
        def property_schema_statement(connection, schema)
          statement = super
          statement << " COMMENT '#{schema[:comment]}'" if schema[:comment]
          statement
        end
      end

      include CommentExtensions
    end
  end
end

# dm-migrations-1.2.0/lib/dm-migrations/sql/mysql.rb
# create_table、propertyの定義部分にコメントを差し込む
# migrationのための対応
module SQL
  module Mysql
    def table_options(opts)
      opt_engine    = opts[:storage_engine] || storage_engine
      opt_char_set  = opts[:character_set] || character_set
      opt_collation = opts[:collation] || collation
      opt_comment = opts[:comment] || ''

      " ENGINE = #{opt_engine} CHARACTER SET #{opt_char_set} COLLATE #{opt_collation} COMMENT='#{opt_comment}'"
    end

    def property_schema_statement(connection, schema)
      if supports_serial? && schema[:serial]
        statement = "#{schema[:quote_column_name]} SERIAL PRIMARY KEY"
        statement << " COMMENT '#{schema[:comment]}'" if schema[:comment]
        statement
      else
        super
      end
    end
  end
end

auto_migrateの場合

auto_migrateの場合、モデルに次のような定義行う。propertyの部分にコードとしてコメントがあるのはわかりやすい。テーブル名の定義はちょっとださい。

# -*- coding: utf-8 -*-
class User
  include DataMapper::Resource

  def self.table_comment; 'ユーザー'; end
  property :id, Serial, comment: 'ユーザーID'
  property :name, String, comment: 'ユーザー名'
end

# CREATE TABLE `users` (
#   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ユーザーID',
#   `name` varchar(50) DEFAULT NULL COMMENT 'ユーザー名',
#   PRIMARY KEY (`id`),
#   UNIQUE KEY `id` (`id`)
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ユーザー'

migrationの場合

db/migrate以下にマイグレーションスクリプトを書く場合は、次のように定義する。

# -*- coding: utf-8 -*-
migration 1, :create_users do

  up do
    create_table :users, comment: 'ユーザー' do
      column :id, Integer, serial: true, comment: 'ユーザーID'
      column :name, String, comment: 'ユーザー名'
    end
  end

  down do
    drop_table :users
  end

end

# -*- coding: utf-8 -*-
migration 2, :modify_name_comment do

  up do
    modify_table :users do
      change_column :name, String, comment: '名前'
    end
  end

  down do
    modify_table :users do
      change_column :name, String, comment: 'ユーザー名'
    end
  end

end

# migration 1
# CREATE TABLE `users` (
#   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ユーザーID',
#   `name` varchar(50) DEFAULT NULL COMMENT 'ユーザー名',
#   PRIMARY KEY (`id`),
#   UNIQUE KEY `id` (`id`)
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ユーザー'

# migration 2
# ALTER TABLE `users` MODIFY COLUMN `name` VARCHAR(50) COMMENT '名前'

ここまでやったが。。。

  • dm-migrations-1.2.0のrename_columnはmysqlでは動かない。master(リリースされていない1.3.x系)では修正されている。
  • 結局、マイグレーションスクリプトのSQLを書いてる。(実際はauto_migrateした結果をコピペ)
  • gemが細かく分割されているため、コードを追うのが大変だった。。。
  • 1.3.0系のリリースや、dm-railsのrails4対応ってあるのかなぁ。。