Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

ドキュメントのシーケンシャルデータからコードの断片を生成する

臭い名

ドキュメントに存在するシーケンシャルなデータからコードの断片を生成する

臭い英名

sequential data in document

臭い状況

仕様書や要件定義書や資料をもとにコードを記述する機会があります。
仕様書等に大量のシーケンシャルな値がある場合、目で見て一つ一つコードを記述すると
時間がかかるものです。
コピペするにしても数が多いと手間がかかります。

この場合、ドキュメントのシーケンシャルデータとソースコードの形式が一定のルールでひも付いていれば
変換用のスクリプトを用いてコードを書くことが可能になります。

リファクタリング

シーケンシャルデータからコードの断片を生成

リファクタリング英名

generate code fragment from sequential data in document

改善理由

・手作業によるミスをなくす
・手作業による作業時間を減らす
・仕様変更時の工数を減らす

対応

・ドキュメントのシーケンシャルデータをリストに抽出する
・シーケンシャルデータをループで処理しながらコードのボイラーテンプレートにはめ込む
・結果をコピーしてソースコードにはめ込む
ソースコードのパースが容易なら処理結果をコードに埋め込むスクリプトを生成するとさらに効率が上がる

サンプル

仕様

Excelにある大量のシート名とソート順の組み合わせをJavaのMapとして定義する。
ExcelのシートからRubyでシート名の一覧を取得する=>下記記事参照
Ruby | Excelのシート名を一括取得し、標準出力する
http://d.hatena.ne.jp/tbpg/20130625/1372169541
・取得したシート名リストを元にコードテンプレートを適用した大量のSheetsSortのコンストラクタ呼び出しのコードを生成する。
・結果をJavaのクラスにペーストする

処理概要シーケンス

Map設定用クラス

「// #start#、// #end#」の間に自動生成したコードを反映する
再実行ができるように「// #start#、// #end#」は生成後も残しておく

class SheetsSortSetting {
  public Map<SheetsSort> getSheetsSort() {
    Map<String, Integer> map = new HashMap<>();
// #start#

// #end#
    return map;
  }
}
シート名の一覧取得(excel_sheets.rb)は下記参照

Ruby | Excelのシート名を一括取得し、標準出力する
http://d.hatena.ne.jp/tbpg/20130625/1372169541

ソースコード生成用スクリプト(map_code_generator.rb)
# encoding: utf-8

require_relative "standard_io"
require "pp"

class SourceReplacer
  include StandardIo
  validate_file 0

  def replace_placeholder(replace_code)
    output_file = $*[0]
    source = read_file(output_file)
    write_file(output_file, source, replace_code)
  end

  def read_file(output_file)
    source = ""
    File.open(output_file, "r")  do |f|
      source = f.read
    end
    return source
  end

  def write_file(output_file, source, replace_code)
    source.gsub!(%r|// #start#(.*\n){0,}// #end#|, "// #start#\n#{replace_code}\n// #end#")

    File.open(output_file, "w+")  do |f|
      f.print source
    end
  end
end

replace = ""
$*[1..($*.size)].each_with_index {|key, cnt|replace << "    map.put(\"#{key}\", #{cnt + 1});\n"}
replace.chop!

sr = SourceReplacer.new
exit unless sr.validate
sr.replace_placeholder(replace)
入力Excel

※実際にこの手法を使う時はもっと大量のシートがあって手作業するのが億劫な時です。
hoge,hage,higeの3シートを用意

実行コマンド
ruby excel_sheets.rb hoge.xlsx | xargs ruby map_code_generator.rb SheeetsSortSetting.java

実行後のSheeetsSortSetting.javaクラス

class SheetsSortSetting {
  public Map<SheetsSort> getSheetsSort() {
    Map<String, Integer> map = new HashMap<>();
// #start#
    map.put("hoge", 1);
    map.put("hage", 2);
    map.put("hige", 3);
// #end#
    return map;
  }
}
入力Excelにシートを追加

poge,page,pigeの3シートを用意

実行後のSheeetsSortSetting.javaクラス

class SheetsSortSetting {
  public Map<SheetsSort> getSheetsSort() {
    Map<String, Integer> map = new HashMap<>();
// #start#
    map.put("hoge", 1);
    map.put("hage", 2);
    map.put("hige", 3);
    map.put("poge", 4);
    map.put("page", 5);
    map.put("pige", 6);
// #end#
    return map;
  }
}

備考

もう一段階進めてbat or shell化しておけばrake,gradle,mavenと連携してもう一段階自動化できます。
それはあくまでrake等の機能なので今回は割愛。