Ruby + ERB でコードジェネレータ

仕様変更でテーブル定義が変更されたときに、テーブルに対応するデータクラスや ViewModel を手作業で修正するのは非常に面倒なので、Ruby でコードジェネレータを作成してみた。

テンプレートエンジンには erb を採用。Visual Studio には T4 テンプレートがあるけど、スクリプト言語でサクッと作りたかったので、採用は見送り。Ruby なら標準で erb があるからね。

ひとまず、YAML で書いたテーブル定義をもとにソースコードを生成するようにしてみた。

require 'rubygems'
require 'yaml'
require 'erb'

class Table
  attr_accessor :name
  attr_accessor :alias_name
  attr_accessor :summary
  attr_accessor :columns

  def initialize
    @columns = []
  end
end

class Column
  attr_accessor :summary
  attr_accessor :summary_detail
  attr_accessor :alias_name
  attr_accessor :name
  attr_accessor :sqltype
  attr_accessor :default
  attr_accessor :null
  attr_accessor :index
  attr_accessor :primary

  # SQL Type から .NET Type を求める
  def type
    case @sqltype
    when "bigint" then "long"
    when "bit" then "bool"
    when "int" then "int"
    when "text" then "string"
    when "nvarchar" then "string"
    when "varchar" then "string"
    when "datetime" then "DateTime"
    when "decimal" then "decimal"
    when "float" then "float"
    when "smallint" then "short"
    when "tinyint" then "byte"
    when "uniqueidentifier" then "Guid"
    end
  end

  def null?
    @null == true
  end

  def index?
    @index == true
  end

  def primary?
    @primary == true
  end
end

class Generator
  attr_accessor :tables

  def initialize(template_path, output_path)
    @tables = []
    @template_path = template_path
    @output_path = output_path
  end

  def generate(tables)
    @tables = tables
    template = File.open(@template_path) { |f| f.read }
    ret = ERB.new(template, nil, '-').result(binding)
    File.open(@output_path, mode="w") { |f| f.write(ret) }
  end
end

class YamlReader
  def read_tables(file_path)
    tables = []

    YAML.load_documents(File.open(file_path)) do |obj|
      obj["tables"].each do |table|
        tbl = Table.new
        tbl.name = table["name"]
        tbl.alias_name = table["alias_name"]
        tbl.summary = table["summary"]

        table["columns"].each do |column|
          col = Column.new
          col.name = column["name"]
          col.alias_name = column["alias_name"]
          col.sqltype = column["sqltype"]
          col.summary = column["summary"]
          col.primary = column["primary"]
          tbl.columns << col
        end

        tables << tbl
      end
    end

    return tables
  end
end

if ARGV.size < 3 then
  puts 'arguments error!'
  puts 'codegen.rb "input file" "template file" "output file"'
else
  input_file = ARGV[0]
  template_file = ARGV[1]
  output_file = ARGV[2]

  reader = YamlReader.new
  tables = reader.read_tables(input_file)

  gen = Generator.new(template_file, output_file)
  gen.generate(tables)
end

最終目標は Excel で書いたテーブル定義を読み込んでソースコードを出力することだけど、実装にはまだ時間がかかりそうだ。