Hatena::ブログ(Diary)

脱サラ大学生のプログラム日記 RSSフィード

2016-09-26

Seasar Conference 2016 Finalに行ってきた

| 10:45

行ってきました!

あいにくの雨でしたが、久しぶりのConference。

気がつけばはてなも何年放置していたんだろう?

SEASAR PROJECTふりかえり

ひがさんがこれまでのプロジェクトを振り返って、当時どうおもって作っていたのかを話してくれました。

SeasarからS2Daoときて、失敗したといっていたS2JSFTeeda、、、

そしてS2Strutsを採用しようとしたプロジェクト用にもっと使いやすい仕組みとして作ったSAStrutsS2JDBC

Slim3の話も聞きたかったな

Teedaは失敗だった?

ひがさん的には失敗だったみたいです。ただJSFって規格に乗っかったのが原因かな?

個人的にはテンプレート部分がすごく好きです。

テンプレートのHTMLがブラウザでそのまま確認できて、組み込みやすいと思うんですよね。

ただ、JSFの影響がいろいろでてきている、、、

DOCTYPEがちょっと変だとエラーになったり、出力されるIDなどがJSF規格なので、JavaScriptと相性が悪かったりといろいろありました。

あとはSeasar全般でチュートリアルプログラムの中をみると使い方がわかるけれど、逆引きとかだとどうデータをいれていいのかわからなかったり、セッションとかの使い方がわかりにくかったりとかして、みんな同じところでハマっていた気がします。

とはいえ、テンプレートエンジンとしては好きです

Mayaaとかはあまり触ったことないですが、やっぱりTeedaの方が直感的かなって思います

ためにしJQueryでYAML形式かJSON形式のデータを読み込んでTeedaテンプレートをブラウザだけでどのように動くのか確認するもの作ってみましたが便利でした。

実際には使っていなくて、テストだけで終わりましたが、今度なにかで使えたらいいな

SEASARのフォークの仕方、LASTAFLUTE

まだ、フォークしていないです。ごめんなさい

そして、たぶんフォークしません

そういえばSeasarをビルドするのに昔にJava4をいれたなと思い出しました。

最近その環境消した気もします、、、

Java8化したときには結構いろいろ手をいれないといけないみたいですね。

SASTRUTS + S2JDBC から SKINNY FRAMEWORK への移行ガイド

Scalaもたのしそうですね

Javaやっていた人はGoかScalaに移動していった感じなのかな?

ここで次の予定があったので、離脱

久しぶりのカンファレンスは非常に楽しかったです!

2012-02-14

EC-CUBEの納品用画面仕様書作成

| 23:06

ソースファイルから仕様書の自動作成実験です。EC-CUBEのソースからほぼ自動で画面仕様と画面キャプチャーを行なっています。

作成したPDF(Google Doc)

↑ファイル->元のファイルをダウンロードからacrobatでしおり表示して見たほうが見やすいです。

f:id:akiraneko:20120214232803p:image

↑こんな感じのページになります

ページクラスの抜き出し

LC_Pageから始まるファイルをすべて検索して処理を行います。中のファイルを見てみると以下のような行がありますので、ここを抜き出します。

<?php
$objFormParam->addParam('会社名', 'company_name', STEXT_LEN, 'KVa',  array("MAX_LENGTH_CHECK"));

半自動ツールなので、かなり無茶なコードですが、evalを駆使してデータの取得を行っています。。。上記以外にも必要そうなデータを取得しています。

当初はfgetsを利用して、行単位で取り込みを行なっていましたが、複数行に分割されていると取得できないので、token_get_allを利用してなんちゃってPHPソースのパースを行なっています。

画面キャプチャ用のhtml出力

ファイル名から推測されるURLにアクセスして、画面キャプチャを取得するhtmlを記述します。出力するファイルはSelenium IDE用のファイルで、Firefoxで一気にキャプチャします。

一部URLが自動的に変換できないところなどは対応表作って正しいURLを設定してあげてます。

画面キャプチャ

Selenium IDEを利用して一気にキャプチャします。最初にログインを行ってから、各画面にアクセスしてはキャプチャを繰り返します。プラグインの追加やデリート系のページについては画面キャプチャが面倒なのであきらめました。数枚であれば手で取ればいいですからね。

再度ツール実行

キャプチャした場所にファイルがあると項目のほかに画面キャプチャもhtmlに出力しています。印刷した際に一画面に収まるように縦に分割とかもしています。SEO設定画面は縦に長すぎるので表示していません。

htmlの保存

ローカルにファイルないと次のWordに貼り付けができないので、一度保存します。IE8とFirefox9では正常に保存と読み込みができました。

Wordで読み込み

保存したhtmlを読み込みます。h1タグとかをつけて出力してあるので、そのまま見出しで読み込まれます。

雛形Wordにコピーアンドペーストで貼り付ける

見出しなどに書式が設定されているファイルに貼り付けます。表紙や目次などもあらかじめ設定してあるのがよいと思います。

今回は最初の概要までは手で作って、それ以降はツールから貼り付けたものそのままになります。そのままだとリンク設定になっていますので、図を文章に保存するにしてから、リンクを解除します。

目次の更新

意外と大切!

見出しを選択してからF9ですべて更新します。

PDFに出力

そのままでもいいのですが、Word2007以降の人の場合にはしおり付きのPDFが出力できます。

考察

実は一年以上前に取り込み部分まで作っていたのですが、特に自分では利用しないので忙しさにかまけて放置していました。。。まだツールとしては荒いつくりなので、手を入れる必要があるとは思いますがプロトタイプとしては十分かと思います。

OSS使っても全部のページの仕様書頂戴といわれることが多いと思うので、こんなものがあってもいいかなーって思って作りました。カスタマイズしたところは、もう少し詳細仕様を別紙で起こして、共通部分はこれぐらいでいいよね?

ライセンスどうなっているんだーとか、改造してやるぞとか、使い方わからねーとかはコメントかメッセージ等ください。

気がついたら1年ぐらい経過していました。本当は画面遷移まで取ろうかと思いましたが、テンプレートとページクラスの両方からデータを取得しないといけないのでやめました。そのうち画面遷移図も作るかもしれません。

作成物

作成したPDF(Google Doc)

作成したプログラム

<?php

$def_base_url = "http://localhost/eccube/html";
$def_user_id = "admin";
$def_user_pass = "admin";
$def_user_id2 = "test@test.jp";
$def_user_pass2 = "test";
$def_out_dir = "C:\\p\\xampp\\htdocs\\eccube\\capimg";
$def_img_height = 1200;
$def_img_width = 640;
$def_product_id = 2;

$def_urllist = array( 
	$def_base_url."/admin/index/index.php" => $def_base_url."/admin/index.php",
	$def_base_url."/admin/products/class_category.php" => $def_base_url."/admin/products/classcategory.php",
	$def_base_url."/admin/products/uploadcsv.php" => $def_base_url."/admin/products/upload_csv.php",
	$def_base_url."/admin/products/uploadcsvcategory.php" => $def_base_url."/admin/products/upload_csv_category.php",
	$def_base_url."/admin/system/admin_area.php" => $def_base_url."/admin/system/adminarea.php",
	$def_base_url."/mypage/down_load.php" => $def_base_url."/mypage/download.php",
	$def_base_url."/admin/system/admin_area.php" => $def_base_url."/admin/system/adminarea.php",
	$def_base_url."/products/detail.php" => $def_base_url."/products/detail.php?product_id=".$def_product_id,
	$def_base_url."/products/review.php" => $def_base_url."/products/review.php?product_id=".$def_product_id,
	$def_base_url."/input_zip/index.php" => $def_base_url."/input_zip.php?zip1=100&zip2=0001",
);

$def_nodisp_urllist = array( 
	$def_base_url."/admin/basis/seo.php",
	$def_base_url."/admin/plugin/install.php",
	$def_base_url."/admin/plugin/uninstall.php",
);

ini_set('display_errors', 'Off');

if( isset( $_GET['file'] ) ){
	dispImg();
	exit;
}

$sfp = fopen( "selenium.html", "w" );

require_once 'html/require.php';

define ("DEFAULT_DSN", DB_TYPE . "://" . DB_USER . ":" . DB_PASSWORD . "@" . DB_SERVER . ":" .DB_PORT . "/" . DB_NAME);

define ("CLASS_IMAGE_WIDTH", "CLASS_IMAGE_WIDTH");
define ("CLASS_IMAGE_HEIGHT", "CLASS_IMAGE_HEIGHT");

require_once 'data/mtb_constants_init.php';

$searchEcCube = new searchEcCubeClass;
$searchEcCube->checkDir(".");

$now_h1type = "none";

seleniumHead($sfp);

$pb_flag = false;
foreach( $searchEcCube->checkList as $file ){
	$h1type = getH1Type($file['file']);
	if( $h1type != $now_h1type ){
		$now_h1type = $h1type;
		$print_h1type = getH1typename($now_h1type);
		echo "<h1>$print_h1type</h1>";
		$pb_flag = false;
	}

	if( $pb_flag == false ){
		$pb_flag = true;
	} else {
		echo '<div style="page-break-after:always"></div>'."\n";
	}

$data_file = substr( $file['file'], 1 );
	echo <<<__EOF__
<h2>{$file['title']}</h2>
__EOF__;

	$url = seleniumPage($sfp, $file['file'] );

	preg_match( '/LC_Page_(.*)\.php/', $file['file'], $match );
	$pageName = strtolower( $match[1] );
	$img_file = "capimg/{$pageName}.png";

	list($width, $height, $type, $attr) = getimagesize($img_file);
	$img_filesize = filesize($img_file);
	if( 0 < $height && $height < 3000 ){
		$img_cnt = floor( $height / $def_img_height ) + 1;
		echo '<h3>画面キャプチャ</h3>';
		echo '<table border="1" style="width:'.$def_img_width.'px;"><tr><td>';
		for( $i = 0 ; $i < $img_cnt ; $i++ ){
			$img_file_url = "?file=capimg/{$pageName}.png&ct=$i&h=$def_img_height";

			// 200以下の場合
			$h = $def_img_height;
			if( $img_cnt-1 == $i ){
				$h = $height % $def_img_height;
			}

			$h = floor( $h / ( 1.0 * $width / $def_img_width ) );
			echo '<img src="' . $img_file_url . '" width="'.$def_img_width.'" height="'.$h.'">' . "<br />\n";
		}
		echo "</td></tr></table>\n";
	} else {
//		echo '<h3>画面キャプチャ</h3>';
//		echo '画面なし';
	}

	echo "<h3>ファイル</h3>\n";
	echo $data_file. "<br />\n";

	echo "<h3>URL</h3>\n";
	echo $url. "<br />\n";

	if( count( $file['data2'] ) ){

	echo <<<__EOF__
<h3>パラメータ</h3>
<table border=1 width="100%">
<tr>
	<th bgcolor="gray" width="20%">項目名</th>
	<th bgcolor="gray" width="15%">name</th>
	<th bgcolor="gray" width="10%">長さ</th>
	<th bgcolor="gray" width="10%">コンバート</th>
	<th bgcolor="gray" width="35%">チェック内容</th>
	<th bgcolor="gray" width="15%">デフォルト</th>
	<th bgcolor="gray" width="5%">DB入力</th>
</tr>
__EOF__;

	foreach( $file['data2'] as $data ){
		echo "<tr>\n";

		// 項目名
		echo "<td>";
		echo $data[0];
		echo "</td>\n";

		// name
		echo "<td>";
		echo $data[1];
		echo "</td>\n";

		// 長さ
		echo "<td>";
		echo $data[2];
		echo "</td>\n";

		// コンバート
		echo "<td>";
		echo $data[3];
		echo "</td>\n";

		// チェック項目
		echo "<td>\n";
		echo implode(", ", arrayGetName( $data[4] ) );
		echo "</td>\n";

		// デフォルト
		echo "<td>";
		echo $data[5];
		echo "</td>\n";

		// DB
		echo "<td>";
		echo ($data[6])?"":"";
		echo "</td>\n";

		echo "</tr>\n";
	}

	echo <<<__EOF__
</table>
<br />
__EOF__;
	}

	if( count( $file['data3'] ) ){

	echo <<<__EOF__
<h3>ファイルアップロード</h3>
<table border=1 width="100%">
<tr>
	<th bgcolor="gray" width="20%">項目名</th>
	<th bgcolor="gray" width="15%">name</th>
	<th bgcolor="gray" width="20%">指定する拡張子</th>
	<th bgcolor="gray" width="10%">制限サイズ</th>
	<th bgcolor="gray" width="10%">必須</th>
	<th bgcolor="gray" width="10%">横幅</th>
	<th bgcolor="gray" width="10%">縦幅</th>
	<th bgcolor="gray" width="5%">画像</th>
</tr>
__EOF__;

	foreach( $file['data3'] as $data ){
		echo "<tr>\n";

		// 項目名
		echo "<td>";
		echo $data[0];
		echo "</td>\n";

		// name
		echo "<td>";
		echo $data[1];
		echo "</td>\n";

		// 指定する拡張子
		echo "<td>";
		echo implode(", ", $data[2] );
		echo "</td>\n";

		// 制限サイズ
		echo "<td>";
		echo $data[3];
		echo "</td>\n";

		// 必須
		echo "<td>\n";
		echo $data[4];
		echo "</td>\n";

		// 横幅
		echo "<td>";
		echo $data[5];
		echo "</td>\n";

		// 縦幅
		echo "<td>";
		echo $data[6];
		echo "</td>\n";

		// 画像
		echo "<td>";
		echo $data[7];
		echo "</td>\n";

		echo "</tr>\n";
	}

	echo <<<__EOF__
</table>
<br />
__EOF__;
	}

	if( count( $file['data'] ) ){

	echo <<<__EOF__
<h3>入力チェック</h3>
<table border=1 width="100%">
<tr>
	<th bgcolor="gray" width="20%">項目名</th>
	<th bgcolor="gray" width="10%">必須</th>
	<th bgcolor="gray" width="20%">入力種別</th>
	<th bgcolor="gray" width="10%">範囲チェック</th>
	<th bgcolor="gray" width="40%">チェック内容</th>
</tr>
__EOF__;

	foreach( $file['data'] as $data ){
		echo "<tr>\n";

		// 項目名
		echo "<td>";
		echo $data[0][0];
		echo "</td>\n";

		// 必須
		$checkTypeList = array();
		$checkTypeList[1] = array();
		$checkTypeList[2] = array();
		$checkTypeList[3] = array();
		foreach( $data[1] as $checkName ){
			$type = getCheckType($checkName);
			$checkTypeList[$type][] = $checkName;
		}

		echo "<td>\n";
		echo implode("<br />", arrayGetName( $checkTypeList[1] ) );
		echo "</td>\n";
		echo "<td>\n";
		echo implode("<br />", arrayGetName( $checkTypeList[2] ) );
		echo "</td>\n";
		echo "<td>\n";
		echo implode("<br />", arrayGetName( $checkTypeList[3] ) );
		echo "</td>\n";

		// チェック項目
		array_shift($data[0]);
		echo "<td>\n";
		echo implode(", ", $data[0] );
		echo "</td>\n";

		echo "</tr>\n";
	}

	echo <<<__EOF__
</table>
__EOF__;
	}

}

seleniumFoot($sfp);
fclose($sfp);


class searchEcCubeClass{
	var $arrAllowedTag = "<arrAllowedTag>";
	var $arrReviewDenyURL = "<arrReviewDenyURL>";
	var $checkList = array();
	function lfGetSqlDenyList(){}

	function checkDir($path){
		$list = scandir( $path );
		foreach( $list as $file ){
			$pathname = $path . "/" . $file;

			if( $file == '.' || $file == '..'){
				continue;
			}

			if( is_dir( $pathname ) ){
				$this->checkDir( $pathname );
			} else if( preg_match( "/^LC_Page.*\.php$/", $file ) ){
				$this->checkFile($pathname);
			}
		}
	}

	function checkFile($path){
		$cnt = "#N#";
		$no = "#No#";
        $classcategory_id1 = "classcategory_id#id#_1";
        $classcategory_id2 = "classcategory_id#id#_2";
        $quantity = "quantity<id>";
		$pageTitle = "";
		$i = "#i#";

		$checkList = array();
		$checkList2 = array();
		$checkList3 = array();

		$objErr = new objErr;
		$objWebParam = new objErr;
		$masterData = new masterData;
		$this->objDate = new objDateClass;
		$this->objFormParam = new objErr;
		$this->objUpFile = new objErr;
		$this->lfGetSqlDenyList = new objErr;
		$objUpFile = new objErr;
		$objFormParam = new objErr;
		$objCheckCategory = new objErr;
		$arrList = array(
			"startyear_m" => "#startyear_m#",
			"startmonth_m" => "#startmonth_m#",
			"startyear" => "#startyear#",
			"startmonth" => "#startmonth#",
			"startday" => "#startday#",
			"endyear" => "#endyear#",
			"endmonth" => "#endmonth#",
			"endday" => "#endday#",
		 );

		$filebody = file_get_contents($path);
		$lines = file2lines($filebody);
		foreach( $lines as $line ){
			if( preg_match( "/objErr->doFunc(.*);/", $line ) ){
				eval( $line );
				$checkList[] = array( $objErr->var1, $objErr->var2 );
			} else if( preg_match( "/objCheckCategory->doFunc(.*);/", $line ) ){
				eval( $line );
				$checkList[] = array( $objCheckCategory->var1, $objCheckCategory->var2 );
			} else if( preg_match( "/this->objFormParam->addParam(.*);/", $line ) ){
				eval( $line );

				$checkList2[] = array(
					$this->objFormParam->disp_name,
					$this->objFormParam->keyname,
					$this->objFormParam->length,
					$this->objFormParam->convert,
					$this->objFormParam->arrCheck,
					$this->objFormParam->default,
					$this->objFormParam->input_db
				);
			} else if( preg_match( "/objFormParam->addParam(.*);/", $line ) ){
				eval( $line );
				$checkList2[] = array(
					$objFormParam->disp_name,
					$objFormParam->keyname,
					$objFormParam->length,
					$objFormParam->convert,
					$objFormParam->arrCheck,
					$objFormParam->default,
					$objFormParam->input_db
				);
			} else if( preg_match( "/this->objUpFile->addFile(.*);/", $line ) ){
				eval( $line );
				$checkList3[] = array(
					$this->objUpFile->disp_name,
					$this->objUpFile->keyname,
					$this->objUpFile->arrExt,
					$this->objUpFile->size,
					$this->objUpFile->necessary,
					$this->objUpFile->width,
					$this->objUpFile->height,
					$this->objUpFile->image
				);
			} else if( preg_match( "/objUpFile->addFile(.*);/", $line ) ){
				eval( $line );
				$checkList3[] = array(
					$objUpFile->disp_name,
					$objUpFile->keyname,
					$objUpFile->arrExt,
					$objUpFile->size,
					$objUpFile->necessary,
					$objUpFile->width,
					$objUpFile->height,
					$objUpFile->image
				);
			} else if( preg_match( "/\*(.*)のページクラス/", $line, $match ) ){
				$pageTitle = trim( $match[1] );
			}
		}

		if( count( $checkList ) != 0 || count( $checkList2 ) != 0 || count( $checkList3 ) != 0 ){
			$this->checkList[] = array( "title" => $pageTitle, "file" => $path, "data" => $checkList, "data2" => $checkList2, "data3" => $checkList3 );
		}
	}
}

class objErr{
	var $var1;
	var $var2;

	var $disp_name;
	var $keyname;
	var $length;
	var $convert;
	var $arrCheck;
	var $default;
	var $input_db;

	var $arrExt;
	var $size;
	var $necessary;
	var $width;
	var $height;
	var $image;

	function doFunc( $set_var1, $set_var2 ){
		$this->var1 = $set_var1;
		$this->var2 = $set_var2;
	}

    function addParam($disp_name, $keyname, $length="", $convert="", $arrCheck=array(), $default="", $input_db="true") {
        $this->disp_name = $disp_name;
        $this->keyname = $keyname;
        $this->length = $length;
        $this->convert = $convert;
        $this->arrCheck = $arrCheck;
        $this->default = $default;
        $this->input_db = $input_db;
    }

	function addFile($disp_name, $keyname, $arrExt, $size, $necessary=false, $width=0, $height=0, $image=true){
		$this->disp_name = $disp_name;
		$this->keyname = $keyname;
		$this->arrExt = $arrExt;
		$this->size = $size;
		$this->necessary = $necessary;
		$this->width = $width;
		$this->height = $height;
		$this->image = $image;
	}
}

class masterData{
	function getMasterData(){
		return "#URL#";
	}
}

class objDateClass{
	function getStartYear(){
		return "#StartYear#";
	}
	function getEndYear(){
		return "#EndYear#";
	}
}

function arrayGetName( $array ){
	$ret = array();
	foreach( $array as $data ){
		$ret[] = getName( $data );
	}

	return $ret;
}

function getName( $str ){
	switch( $str ){
		case "EXIST_CHECK": return "必須";
		case "ALL_EXIST_CHECK": return "すべて必須";
		case "TOP_EXIST_CHECK": return "上位が入力済み";
		case "ONE_EXIST_CHECK": return "どれか選択";
		case "FILE_EXISTS": return "ファイルの存在チェック";

		case "NUM_CHECK": return "数値";
		case "GRAPH_CHECK": return "英数記号";
		case "KANA_CHECK": return "カナ";
		case "EMAIL_CHECK": return "E-Mail形式";
		case "MOBILE_EMAIL_CHECK": return "携帯Mail";
		case "EMAIL_CHAR_CHECK": return "E-Mail文字種";
		case "TEL_CHECK": return "電話形式";
		case "ZERO_CHECK": return "0チェック";
		case "ALPHA_CHECK": return "半角英字";
		case "CHECK_DATE": return "日付形式(ymd)";
		case "CHECK_DATE2": return "日付形式(ymd-hm)";
		case "KANABLANK_CHECK": return "カナ(スペース許可)";
		case "ALNUM_CHECK": return "英数";
		case "FILE_NAME_CHECK": return "ファイル名形式";
		case "FILE_NAME_CHECK_BY_NOUPLOAD": return "ファイル名形式(アップロード以外)";
		case "HTML_TAG_CHECK": return "Htmlタグ";
		case "URL_CHECK": return "URL形式";
		case "IP_CHECK": return "IPアドレス";
		case "PROHIBITED_STR_CHECK": return "禁止文字";
		case "SPTAB_CHECK": return "スペース・タブのみ";
		case "NO_SPTAB": return "スペース・タブ";
		case "ZERO_START": return "先頭が0";

		case "MAX_LENGTH_CHECK": return "最大";
		case "NUM_COUNT_CHECK": return "桁数";
		case "CHECK_SET_TERM": return "期間チェック";
		case "CHECK_SET_TERM2": return "期間チェック(秒まで)";
		case "NUM_RANGE_CHECK": return "最小・最大";
		case "GREATER_CHECK": return "値比較(<)";
		case "DIFFERENT_CHECK": return "値比較(!=)";
		case "SELECT_CHECK": return "リスト範囲内";
		case "EQUAL_CHECK": return "一致";
		case "MIN_CHECK": return "最小";
		case "MAX_CHECK": return "最大";
		case "MIN_LENGTH_CHECK": return "最小長チェック";
	}

	return "####" . $str;
}

function getCheckType( $str ){
	switch( $str ){
		case "EXIST_CHECK": // 必須
		case "ALL_EXIST_CHECK": // すべて必須
		case "TOP_EXIST_CHECK": // 上位が入力済み
		case "ONE_EXIST_CHECK": // どれか選択
		case "FILE_EXISTS": // ファイルの存在チェック
			return 1;

		case "NUM_CHECK": // 数値
		case "GRAPH_CHECK": // 英数記号
		case "KANA_CHECK": // カナ
		case "EMAIL_CHECK": // E-Mail形式
		case "MOBILE_EMAIL_CHECK": // 携帯Mail
		case "EMAIL_CHAR_CHECK": // E-Mail文字種
		case "TEL_CHECK": // 電話形式
		case "ZERO_CHECK": // 0チェック
		case "ALPHA_CHECK": // 半角英字
		case "CHECK_DATE": // 日付形式(ymd)
		case "CHECK_DATE2": // 日付形式(ymd-hm)
		case "KANABLANK_CHECK": // カナ(スペース許可)
		case "ALNUM_CHECK": // 英数
		case "FILE_NAME_CHECK": // ファイル名形式
		case "FILE_NAME_CHECK_BY_NOUPLOAD": // ファイル名形式(アップロード以外)
		case "HTML_TAG_CHECK": // Htmlタグ
		case "URL_CHECK": // URL形式
		case "IP_CHECK": // IPアドレス
		case "PROHIBITED_STR_CHECK": // 禁止文字
		case "SPTAB_CHECK": // スペース・タブのみ
		case "NO_SPTAB": // スペース・タブ
		case "ZERO_START": // 先頭が0
			return 2;

		case "MAX_LENGTH_CHECK": // 最大
		case "NUM_COUNT_CHECK": // 桁数
		case "CHECK_SET_TERM": // 期間チェック
		case "CHECK_SET_TERM2": // 期間チェック(秒まで)
		case "NUM_RANGE_CHECK": // 最小・最大
		case "GREATER_CHECK": // 値比較(<)
		case "DIFFERENT_CHECK": // 値比較(!=)
		case "SELECT_CHECK": // リスト範囲内
		case "EQUAL_CHECK": // 一致
		case "MIN_CHECK": // 最小
		case "MAX_CHECK": // 最大
		case "MIN_LENGTH_CHECK": // 最小長チェック
			return 3;
	}

	return -1;
}

function getH1Type( $file ){
	if( preg_match( "/\/data\/class\/pages\/(.*?)\//", $file, $match ) ){
		$ret = trim( $match[1] );
		return $ret;
	}

	return null;
}

function getH1typename( $name ){
	switch( $name ){
		case "": return "全体";
		case "admin": return "管理者";
		case "regist": return "登録";
		case "shopping": return "ショッピング";
		case "products": return "商品";
		case "campaign": return "キャンペーン";
		case "contact": return "お問い合わせ";
		case "entry": return "会員登録";
		case "inquiry": return "アンケート";
		case "mypage": return "MyPage";
		case "frontparts": return "フロントパーツ";
		case "tb": return "トラックバック";
		case "cart": return "カート";
		case "forgot": return "パスワード発行";
	}

	return "####" . $name;
}

function seleniumHead( $fp ){
	global $def_base_url;
	global $def_user_id;
	global $def_user_pass;
	global $def_user_id2;
	global $def_user_pass2;
	global $def_out_dir;
	global $def_product_id;

	$str =<<<__ECHO__
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>test</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><td rowspan="1" colspan="3">test</td></tr>
</thead><tbody>

<tr>
	<td>open</td>
	<td>{$def_base_url}/mypage/login.php</td>
	<td></td>
</tr>
<tr>
	<td>type</td>
	<td>css=dd &gt; input[name=&quot;login_email&quot;]</td>
	<td>$def_user_id2</td>
</tr>
<tr>
	<td>type</td>
	<td>css=dd &gt; input[name=&quot;login_pass&quot;]</td>
	<td>$def_user_pass2</td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>id=log</td>
	<td></td>
</tr>

<tr>
	<td>open</td>
	<td>{$def_base_url}/products/detail.php?product_id={$def_product_id}</td>
	<td></td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>id=cart</td>
	<td></td>
</tr>

<tr>
	<td>open</td>
	<td>{$def_base_url}/admin/</td>
	<td></td>
</tr>
<tr>
	<td>type</td>
	<td>login_id</td>
	<td>$def_user_id</td>
</tr>
<tr>
	<td>type</td>
	<td>password</td>
	<td>$def_user_pass</td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>css=span</td>
	<td></td>
</tr>

__ECHO__;

	fwrite( $fp, $str );
}

function seleniumFoot( $fp ){
	$str =<<<__ECHO__
</tbody></table>
</body>
</html>
__ECHO__;
	fwrite( $fp, $str );
}

function seleniumPage( $fp, $name ){
	global $def_base_url;
	global $def_out_dir;
	global $def_urllist;
	global $def_user_id;
	global $def_user_pass;
	global $def_nodisp_urllist;

	preg_match( '/LC_Page_(.*)\.php/', $name, $match );
	$match[1] = preg_replace('/([A-Z][a-z]+)([A-Z][a-z]+)$/', '$1#$2', $match[1]);
	$pageName = strtolower( $match[1] );
	$pageName = str_replace( "#", "", $pageName );
	$pagePath = strtolower( $match[1] );
	$pagePath = str_replace( "_", "/", $pagePath,$ct );
	$pagePath = str_replace( "#", "_", $pagePath );
	$url = "{$def_base_url}/$pagePath";
	
	if( strpos($pagePath, "admin/") === 0 && $ct === 1 ){
		// index
		$url .= '/index.php';
	} else if( $ct === 0 ) 
		// index
		$url .= '/index.php';
	else {
		$url .= '.php';
	}
	
	if( isset( $def_urllist[ $url ] ) ){
		$url = $def_urllist[ $url ];
	}
		
	if( array_search( $url, $def_nodisp_urllist ) === false ){
		$str =<<<__ECHO__

<tr>
	<td>open</td>
	<td>$url</td>
	<td></td>
</tr>
<tr>
	<td>captureEntirePageScreenshot</td>
	<td>{$def_out_dir}\\{$pageName}.png</td>
	<td></td>
</tr>

__ECHO__;
	}

	// ログイン画面
	if( $url === $def_base_url . '/admin/index.php' ){
		$str .=<<<__ECHO__
<tr>
	<td>type</td>
	<td>login_id</td>
	<td>$def_user_id</td>
</tr>
<tr>
	<td>type</td>
	<td>password</td>
	<td>$def_user_pass</td>
</tr>
<tr>
	<td>clickAndWait</td>
	<td>css=span</td>
	<td></td>
</tr>
__ECHO__;
	}

	fwrite( $fp, $str );
	
	return $url;
}

function dispImg(){
	$src_img = imagecreatefrompng($_GET['file']);
	$img_height = @$_GET['h'];
	if( $img_height == 0 ){
		$img_height = 200;
	}
	$top = @$_GET['ct'] * $img_height;
	$src_width = imagesx($src_img);
	$src_height = imagesy($src_img);

	$img_width = $src_width;

	if( $src_height < $top + $img_height ){
		$img_height = $src_height % $img_height;
	}

	$dst_img = imagecreatetruecolor($img_width, $img_height);
	imagecopy($dst_img, $src_img, 0, 0, 0, $top, $img_width, $img_height);

	header('Content-Type: image/png');
	imagepng( $dst_img );
	imagedestroy($dst_img);
}

function file2lines($file){
	$token = token_get_all($file);
	
	$lines = array();
	$line = '';
	$case_flag = false;
	foreach($token as $key=>$data){
		if( is_string($data) ){
			if( $data === '{' || $data === '}' || ( $case_flag === true && $data === ':' )
				|| $data === ';' )
			{
				$lines[] = $str = ereg_replace("\r|\n","", trim( $line . $data ) );
				$line = '';
			} else {
				$line .= $data;
			}
		} else {
			$skip_token = array(
				T_COMMENT, T_DOC_COMMENT,
				T_OPEN_TAG,
			);
	
			if( $data[0] == T_CASE || $data[0] == T_DEFAULT ){
				// case start
				$case_flag = true;
			} else if( array_search( $data[0], $skip_token ) !== false){
				// reg
				$lines[] = ereg_replace("\r|\n","", trim( $line . $data[1] ) );
				$line = '';
			} else {
				$line .= $data[1];
			}
			$token[$key][99] = token_name($data[0]);
		}
	}

	return $lines;
}

2011-03-22

Teedaネスト対応 途中経過

| 00:17

コミッタにならさせていただきました!

これから頑張ります

いろいろ考えてはいるのですが、ダイナミックプロパティとかに対応しないとダメですね。完全に想定から抜けていました。

利用するための条件

デフォルトは利用しないにしたいと思っています。やっぱり後方互換性がなくなるのと、処理的に遅くなる可能性があるからです。大抵のプロジェクトは利用しても問題はでないとは思っています。

設定自体は teedaCustomize.dicon を利用したいかなと思っています。

ここはDoltengに入るファイルにも影響でますね

利用方法

まだ悩み中ですが_で区切った名前があったらメンバーのプロパティーを調べるのがいいかなと思っています。-とかの文字がいいんだけれど、それ使うと既存の処理にかむので手を入れる範囲が広がりそうですし、処理的に遅くなりそうです。

暫定影響範囲

  • 利用しない場合でも若干チェックロジックが遅くなる
  • 設定ファイル変更(teedaCustomize.dicon)
  • テスト追加
  • マニュアル追記(ここ結構重要)
  • ダイナミックプロパティ対応(getAaaBbbStyle形式?)
  • Doltengの初期設定ファイル変更
  • Doltengのマーカー対応(_が入ったものは一番先頭のプロパティーに関連付け)
  • Doltengの自動生成対応(_が入ったものは自動生成対象にしない?)

んー、まだ抜けているきがしますが走り始めてから考えます。ブランチで作業していたら、一回全部消してから組みなおしてもいいですしね!

2011-02-23

Teedaネスト対応

| 01:15

Teedaでネストしたプロパティー表示の実験

これで実験してから一年半ぐらい経過していますが、組んでみました。

	data3Dto = new Data3Dto();
	data3Dto.data5 = "DataText5";
	data3Dto.data6 = "DataText6";
	data3Dto.data2Dto = new Data2Dto();
	data3Dto.data2Dto.data3 = "DataText3";
	data3Dto.data2Dto.data4 = "DataText4";
	data3Dto.data2Dto.dataDto = new DataDto();
	data3Dto.data2Dto.dataDto.data1 = "DataText1";
	data3Dto.data2Dto.dataDto.data2 = "DataText2";

こんな感じのデータが

<span id="data3Dto_data5">data3Dto_data5</span><br />
<span id="data3Dto_data6">data3Dto_data6</span><br />
<span id="data3Dto_data2Dto_data3">data3Dto_data2Dto_data3</span><br />
<span id="data3Dto_data2Dto_data4">data3Dto_data2Dto_data4</span><br />
<span id="data3Dto_data2Dto_dataDto_data1">data3Dto_data2Dto_dataDto_data1</span><br />
<span id="data3Dto_data2Dto_dataDto_data2">data3Dto_data2Dto_dataDto_data2</span><br />
<span id="data3Dto_data2Dto_dataDto_data3">data3Dto_data2Dto_dataDto_data3</span><br />

こんな感じで利用できます。

最後のdata3Dto_data2Dto_dataDto_data3はプロパティーがないので、データが入らない状態ででます。標準だと例外エラーになるのでここのチェックが一番面倒だと思います。

以下パッチですが、

http://akira.info/labs/NestTeeda/teeda.patch

http://akira.info/labs/NestTeeda/teedaNest20110224.lzh

ここにパッチと、サンプルで動いているプロジェクト起きました。

Eclipseのパッチはなんかおかしい。。。ひとつだけ適応されないところがでてきています(涙)

もう少しテストを書く必要があるけれど、普通にデータ表示するだけだったらこれでいいのかな?

性能実験やっていないので、もう少し速度的な最適化はできるきがします。


Index: teeda-extension/src/test/java/org/seasar/teeda/extension/html/impl/TagProcessorAssembleImplTest.java
===================================================================
--- teeda-extension/src/test/java/org/seasar/teeda/extension/html/impl/TagProcessorAssembleImplTest.java	(revision 4307)
+++ teeda-extension/src/test/java/org/seasar/teeda/extension/html/impl/TagProcessorAssembleImplTest.java	(working copy)
@@ -450,6 +450,10 @@
             return false;
         }
 
+        public boolean hasNestProperty(String name) {
+            return false;
+        }
+
         public boolean isModified() {
             return false;
         }
Index: teeda-extension/src/test/java/org/seasar/teeda/extension/html/impl/page/AaaDto.java
===================================================================
--- teeda-extension/src/test/java/org/seasar/teeda/extension/html/impl/page/AaaDto.java	(revision 4307)
+++ teeda-extension/src/test/java/org/seasar/teeda/extension/html/impl/page/AaaDto.java	(working copy)
@@ -19,10 +19,11 @@
 
 /**
  * @author higa
- * 
+ *
  */
 
 public class AaaDto implements Serializable {
 
     public static final String COMPONENT = "instance=session";
+    public String aaaBbb;
 }
Index: teeda-extension/src/test/java/org/seasar/teeda/extension/html/impl/PageDescImplTest.java
===================================================================
--- teeda-extension/src/test/java/org/seasar/teeda/extension/html/impl/PageDescImplTest.java	(revision 4307)
+++ teeda-extension/src/test/java/org/seasar/teeda/extension/html/impl/PageDescImplTest.java	(working copy)
@@ -43,6 +43,15 @@
         assertFalse(pd.hasProperty(null));
     }
 
+    public void testHasNestProperty() throws Exception {
+        PageDesc pd = createPageDesc(FooPage.class, "fooPage");
+        assertFalse(pd.hasNestProperty("aaa"));
+        assertFalse(pd.hasNestProperty("aaaDto"));
+        assertTrue(pd.hasNestProperty("aaaDto_aaaBbb"));
+        assertFalse(pd.hasNestProperty(null));
+    }
+
+
     public void testHasItemsProperty() throws Exception {
         PageDesc pd = createPageDesc(FooPage.class, "fooPage");
         assertTrue(pd.hasItemsProperty("cccItems"));
Index: teeda-extension/src/main/java/org/seasar/teeda/extension/html/impl/PageDescImpl.java
===================================================================
--- teeda-extension/src/main/java/org/seasar/teeda/extension/html/impl/PageDescImpl.java	(revision 4307)
+++ teeda-extension/src/main/java/org/seasar/teeda/extension/html/impl/PageDescImpl.java	(working copy)
@@ -17,6 +17,7 @@
 
 import java.io.File;
 import java.io.Serializable;
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
@@ -72,6 +73,8 @@
 
     private static final String[] EMPTY_SCOPES = new String[0];
 
+    private Class pageClass;
+
     public PageDescImpl(Class pageClass, String pageName) {
         this(pageClass, pageName, null);
     }
@@ -90,6 +93,8 @@
     }
 
     protected void setup(Class pageClass) {
+        this.pageClass = pageClass;
+
         BeanDesc beanDesc = BeanDescFactory.getBeanDesc(pageClass);
         for (int i = 0; i < beanDesc.getPropertyDescSize(); ++i) {
             PropertyDesc pd = beanDesc.getPropertyDesc(i);
@@ -172,6 +177,57 @@
         return propertyNames.contains(name);
     }
 
+    public boolean hasNestProperty(String name) {
+        if( name == null ){
+            return false;
+        }
+
+        BeanDesc beanDesc = BeanDescFactory.getBeanDesc(pageClass);
+        String[] items = name.split("_");
+
+        if( items.length < 2 ){
+            return false;
+        }
+
+        for (int i = 0; i < beanDesc.getPropertyDescSize(); ++i) {
+            PropertyDesc pd = beanDesc.getPropertyDesc(i);
+            String propertyName = pd.getPropertyName();
+            if( items[0].equals(propertyName) ){
+                String[] items2 = new String[items.length-1];
+                for( int j = 0 ; j < items.length-1; j++ ){
+                    items2[j] = items[j+1];
+                }
+                return hasNestPropertySub( items2, pd.getPropertyType() );
+            }
+
+        }
+
+        return false;
+    }
+
+    private boolean hasNestPropertySub(String[] items,Class propertyClass) {
+        Field propertyFields[] = propertyClass.getDeclaredFields();
+
+        for( int i = 0 ; i < propertyFields.length ; i++ ){
+            String fieldsName = propertyFields[i].getName();
+            if( items[0].equals(fieldsName) ){
+                if( items.length == 1 ){
+                    return true;
+                } else {
+                    String[] items2 = new String[items.length-1];
+                    for( int j = 0 ; j < items.length-1; j++ ){
+                        items2[j] = items[j+1];
+                    }
+                    Class fieldsClass = propertyFields[i].getType();
+
+                    return hasNestPropertySub( items2, fieldsClass );
+                }
+            }
+        }
+
+        return false;
+    }
+
     public boolean hasItemsProperty(String name) {
         return itemsPropertyNames.contains(name);
     }
Index: teeda-extension/src/main/java/org/seasar/teeda/extension/html/PageDesc.java
===================================================================
--- teeda-extension/src/main/java/org/seasar/teeda/extension/html/PageDesc.java	(revision 4307)
+++ teeda-extension/src/main/java/org/seasar/teeda/extension/html/PageDesc.java	(working copy)
@@ -24,6 +24,8 @@
 
     boolean hasProperty(String name);
 
+    boolean hasNestProperty(String name);
+
     /**
      * ForEach繧ТelectOneMenu縺ァ縺ョPage繧ッ繝ゥ繧ケ縺ォ謖√▽Collection縺後≠繧句?蜷医↓true繧定ソ斐☆.
      * @param name
Index: teeda-extension/src/main/java/org/seasar/teeda/extension/html/factory/OutputTextFactory.java
===================================================================
--- teeda-extension/src/main/java/org/seasar/teeda/extension/html/factory/OutputTextFactory.java	(revision 4307)
+++ teeda-extension/src/main/java/org/seasar/teeda/extension/html/factory/OutputTextFactory.java	(working copy)
@@ -67,6 +67,9 @@
         if (isLabel(id, elementNode)) {
             return true;
         }
+        if (isNest(id, pageDesc)) {
+            return true;
+        }
         return pageDesc.hasProperty(id);
     }
 
@@ -88,6 +91,11 @@
         if (pageDesc.hasProperty(id)) {
             properties.put(JsfConstants.VALUE_ATTR, getBindingExpression(
                     pageDesc.getPageName(), id));
+        } else if( isNest(id, pageDesc) ){
+            String[] items = id.split("_");
+            String itemName = implode( items, "." );
+            properties.put(JsfConstants.VALUE_ATTR, getBindingExpression(
+                    pageDesc.getPageName(), itemName));
         } else {
             final String key = toNormalizeId(id);
             TextNode firstTextNode = elementNode.getFirstTextNode();
@@ -96,6 +104,26 @@
         }
     }
 
+    private static String implode(Object[] array, String sep) {
+        if (array == null) {
+            return null;
+        } else if (array.length < 1) {
+            return "";
+        } else if (array.length < 2) {
+            return (array[0] != null) ? array[0].toString() : "";
+        }
+
+        StringBuffer buf = new StringBuffer(
+                (array[0] != null) ? array[0].toString() : "");
+
+        for (int i = 1; i < array.length; i++) {
+            buf.append(sep);
+            buf.append((array[i] != null) ? array[i].toString() : "");
+        }
+
+        return buf.toString();
+    }
+
     protected boolean isLabel(final String id, final ElementNode elementNode) {
         final String key = toNormalizeId(id);
         if (!TeedaExtensionConfiguration.getInstance().outputTextLabelUnderAnchorOnly) {
@@ -115,6 +143,20 @@
         }
     }
 
+    protected boolean isNest(final String id, PageDesc pageDesc) {
+        if( id == null ){
+            return false;
+        }
+
+        String[] items = id.split("_");
+
+        if( items.length < 2 ){
+            return false;
+        }
+
+        return pageDesc.hasNestProperty(id);
+    }
+
     protected String toNormalizeId(String id) {
         final int pos = id.lastIndexOf('-');
         if (pos >= 0) {

2010-07-20

EthnaのURLを綺麗にする(Ethna_UrlHandlerを使う方法)

| 19:55

やっぱりEthnaのURLって微妙だよね?

てなことで、前々から興味があったけれどやっていなかったのを調べてみました。

似た話題

http://rd.uniba.jp/blog/2010/06/30/ethna-%E3%81%A7%E7%B6%BA%E9%BA%97%E3%81%AA-url-ethna_urlhandler-%E3%82%92%E4%BD%BF%E3%82%8F%E3%81%AA%E3%81%84%E6%96%B9%E6%B3%95/

Ethna で綺麗な URL (Ethna_UrlHandler を使わない方法)

ここが最初に検索するとひっかかります。

うん、いまちゃんと読んだけれどこっちの方が楽です(笑)

でも。。。

example.org/hogehoge/fugagufa/?id=10

こうじゃなくって

example.org/hogehoge/fugagufa/10

こうしたいときってありますよね?

まあ、私はあまりないのですが(汗)

方針

Ethna_UrlHandlerがあるので、Ethna_UrlHandlerを拡張して再現したいと思います。ただし、Ethna_UrlHandlerは事前にすべてのアクションを登録しておく必要があるので、非常に面倒です。。。

流れ的にはアクセスしてきたURLをみて、動的にアクションとの紐付けを設定してあげます。

コード(pjname\app\Pjname_Testethna_UrlHandler.php)

<?php
  function &getInstance($class_name = null)
  {
    $instance =& parent::getInstance(__CLASS__);

    // add
    $path = parent::_normalizePath($_SERVER['PATH_INFO']);
    $acrion_list = split( "/", $path[0] );

    $ctl = &Ethna_Controller::getInstance();

    while( count($acrion_list) ){
      $action_name = implode( "_", $acrion_list );

      if( $ctl->getActionClassName($action_name) ){
        break;
      }

      array_pop($acrion_list);
    }
    $url_path = implode( "/", $acrion_list );

    $instance->action_map = array(
      $_SERVER['URL_HANDLER'] => array(
        $action_name => array(
          'path'     => $url_path,
          'path_regexp'  => '|^'.$url_path.'/(.*)$|',
          'path_ext'   => array('url_param' => array()),
        ),
      ),
    );
    // add-end

    return $instance;
  }

上記の部分に無理やり突っ込んでいます。内容はPATH_INFOを取ってきて、整形後にgetActionClassName()を利用して、実在するAction名を探して、それ以降の/はパラメータとしています。

準備

http://ethna.jp/ethna-document-dev_guide-urlhandler.html

ここを参考にして、事前準備が必要です。

$action_map の設定

ここは動的に作っているので必要ありません

_getPath_Index() 関数の定義

ここは動的に作成できないので、{url}を使うことができません。なので設定の必要はありません。

URL_HANDLER 変数の設定

これは必要です。

$config['url'] の設定

なくても動きますが、設定したほうがよいかもしれません。(未検証)

echo_msgアクションを追加

対象アクションは必要です。

値の受け取り方とアクセスURL

example.org/index.php?action_echo_msg=true

通常のURLが上記の場合

example.org/index.php/echo/msg

でアクセスができるようになります。サーバーによっては拡張子を除くことができるので

example.org/index/echo/msg

でも可能です。index.phpの名前を変更することでもっと短い名前にもできますし、mod_rewriteを利用して無くすことも可能ですね。

パラメーターの受け取り方

<?php
    var $form = array(
        'url_param' => array(),
    );

上記のように設定することで index.php/echo/msg/123/456/789 のアクセスの場合 123/456/789 が取り出せます。このままだと使いにくいので

<?php
    var $form = array(
        'num' => array(
            'type'        => VAR_TYPE_INT,
	    'form_type' => FORM_TYPE_TEXT
	),

        'url_param' => array(),
    );

上記のように分解して入れる変数を用意し

<?php
    function prepare()
    {
        $_url_param = split( "/", $this->af->get('url_param' ) );
        $this->af->set('num', $_url_param[0] );

        var_dump($this->af->validate());

        return null;
    }

上記のように分解してから、パラメータを代入してvalidateをかけることで安全に利用することができます。もしくは index.php/echo/msg/0/?num2=1244 とかでもデータは取れます。

総括

本当はパラメータのどこが、どこに対応するのかをまでafの設定の中にいれることで、パラメータの代入まで自動化できると思います。

<?php
    var $form = array(
        'num' => array(
	    'type'        => VAR_TYPE_INT,
	    'form_type' => FORM_TYPE_TEXT,
	    'url_param_num' => 0
        ),
    );

上記みたいに紐付けて、url_param_numがついているパラメータを自動代入がきれいかな。。。

まあ、アクション名をecho_msgみたいな形で妥協すればもっと簡単になるんですが、そこはきれいじゃないですよね?

2010-06-19

JPUG 2010 年夏セミナーにいってきた

| 02:05

http://www.postgresql.jp/events/soukai2010/

PostgreSQL のこれまで、9.0、そしてこれから

非常に興味深いコマでした。ここが今回の目玉ですね!

年表をみて思ったのですが、7.0がない(笑)

私は6系から使っていたのですが、7.0にバージョンアップして痛い目みています(苦笑)

どうも初期はあまり安定していなかったイメージがあって6に戻しました。。。

7.2からVACUUMがロックしなくなったのかな? 個人的にはここが一番大きな転機な気がします!

でも新しい9はかなりの期待! レプリケーションが標準でついて、エンタープライズでも使われるようになるかな? 外国だと何故かMySQLの方がエンタープライズ用途って感じなんですよねー

レプリケーションは9で組むとして、ロードバランサー的なものは何を使った方がいいんだろう?

現状pgpool-IIが9に対応中みたいです。マスターにしか投げないクエリや非同期の誤差が多い場合にはスレイブに投げないなどかなり充実した機能があります!

ただ更新系と参照系で2つコネクション張って、使い分けるんだったらPgBouncerとかPL/Proxyも単機能だからいいよーって、懇談会で知り合いの理事の人に教えていただきました!

むー、ちょっといろいろ検証する必要があるな!

PostgreSQL on Amazon EC2 の可能性

クラウドでの構成例です!

障害時の自動起動の話ですが、たしかにクラウドだといろいろリアルサーバーとは違う運用になりますよね。。。

今度使うクラウドサーバーがどんな仕様かを調べなければ。。。

EC2の場合、待機系を起動しておくとインスタンスの分お金かかるけれど、月7000円を取るかコールドスタンバイで数分待つのどっちがいいのかって話が良かったです(笑)

まあ、この構成の場合にはそう落ちないよね。。。落ちるときには全体が落ちるだろうし(笑)

SRA OSS による PostgreSQL導入事例とサービス活用事例のご紹介

スライド資料なし。

ここはSRAってより石井さんのいる会社ってイメージが強い。。。

VoIP サービスにおけるPostgreSQLの活用

んー

この辺からどんどんPostgreSQLの成分が減ってきます(笑)

やっぱりインフラ系の会社って急にサービスが立ち上がって、いろいろ大変なんだなーって感じでした。。。

ほとんどDB関係ない実装に関しての話ですが、スライドの最後のページは必見!

ポケットの中の PostgreSQL 〜OpenBlockS での活用事例のご紹介〜

これちょっと使ってみたいと昔に思ったんですよね。その時にはHinemosはまだ入っていなかったと思いますが。。。

Hinemos自体は非常に知名度あるけれど、あまり使っているのは聞かない。。。そして情報もないので検証だけして、導入していない事例がかなりな気がします。

もう少し普及してから有料サポートを厚くしていかないとなかなか広がっていかないかもしれませんね。

懇親会

暑かった。。。

あまり沢山の人とは話していないのですが、結構業界狭いですよね(笑)

あとアンケートの結果を速報していましたが、DBとの関わりがDBエンジニア、PG/SE、ITその他みたいな感じだったので、ITその他が多かったみたいです(笑)

私もその他にしました!

PGだけだったら、PGにしますがSEが入るとアプリケーションエンジニアってよりはメンバーってなるので、その他にしました(笑)

たぶんアーキテクトとかがあればもう少し分布がわかったんじゃないかなと思います

2010-04-29

b-mobileSIMについて

| 12:28

徐々に特性についてわかってきました。

普通に使う分にはそんなに不便はないですね。

ただし、何個か制限かけているみたいです。。。

  • flaの拡張子はダウンロード速度が制限されている
  • Windows Updateはできない
  • Skypeのダウンロードも速度制限されている
  • iTunesからiPhoneアプリのも速度制限されている
  • イーバンク銀行にログインできない

mp4とかのダウンロードは大丈夫なんですが、flaの場合秒間1K以下に制限かかっているようです。毎秒900バイトぐらいしかダウンロードできないので、実質動画を見ることができません。

特定のサイトと、特定のファイルについて制限がかかっているみたいですね。

この回線だけで全部まかなうのはちょっと難しいかもしれません。

たまに無線LANとかで他のネットワークに接続して、ダウンロードとかする必要あります。

特にWindows Updateができないので、ちょっと不安かなー。

この辺はb-mobileSIMで制限をかけている場合と、サーバー側で制限をかけている場合があると思いますので、一概にどこがいけないってなかなかわからないのですが、flaファイルは実際同じサーバーからテストでダウンロードしてみたら明らかに遅くなりました。

イーバンク銀行とかは、通信回線からドコモと認識されて、携帯電話用のサイトからログインしてくださいと怒られました。この手のはサーバー側ですね。

ドメインで制限かかっている場合にはダウンロード時だけWebProxyとか使えば問題ないのですが、拡張子とかはちょっと回避方法分かりません。。。

まあ、外出先でネットするみたいな一般用途には十分かなー?

2010-04-27

Eee PC 1005HAGをWindows7にしたい!

| 01:55

ドコモの通信ユニットがWindows7用クライアントを使わないと自動接続できないので、XPを捨てたいのですがちょっと考え中です。

ライセンスはファミリーパックのバージョンアップを持っているので、あと1つあまっています。

ドライバーもオフィシャルサイトに全部あったので問題なし!

ただ。。。バージョンアップに失敗したらネット環境がなくなることと、ドライブがないノートなんですよね(苦笑)

んー

週末にでも覚悟きめてバージョンアップしようかな。。。

2010-04-26

Eee PC 1005HAGとb-mobileSIMの日々

| 00:25

b-mobileSIM買いました!

引越しを気にネット環境がなくなるので、Pocket WiFiの外国版のMiFi 2352とb-mobileSIMを考えていたのですが、やっぱり法律的に問題があるのと、ノートが安ければそっちでもいいんじゃないかと思い、1005HAGを買いました。

良いところ

あんがい300Kでも普通に使えます! これは本当に良かったです。今自宅でもノートパソコン経由でi7のメインマシンから書き込んでいます。

もともとPHSのb-mobileを使っていたのもあるので、遅いのには慣れているんですけれどね(笑)

あと1005HAGのパッテリーが本当に持ちます!

アダブター持ち歩かなくても済むので結構楽ですね。

悪いところ

プロトコル的にはb-mobileSIMは制約がないはずですが、どうも制限があります。SkypeとかのダウンロードだとかWindowsUpdate、iTunesからのアプリダウンロードなどは非常に遅いかダウンロードができません。

これはb-mobileSIMがシェープングをしているのか、サーバー側で制限をかけているのはわかりませんが、動画サイトや特定のサーバー、特定の拡張子のファイルダルんロードは転送速度が一気に遅くなるきがします。

300キロなので、通常秒間30キロぐらいのダウンロードはできるのですが、特定のサイトのみ1キロ以下のダウンロードになったりします。

あとはイーバンクなどの銀行系サイトはモバイルからのアクセス扱いになって、ログインすることができなかったりします。そのため全部をb-mobileSIMでするのは難しくって、無線LANなどを組み合わせてできないこととできることを保管しあうことが必要ですね。

あとは1005HAGのACアダプタが19ボルト出力なのですが、他のと仕様が違うので追加で買うことができません(涙)

同じくDOCOMOが使えてバッテリーが少ない1003HAGは汎用的なのが使えるんですけれどね。。。

あまり外に持ち歩かないのであれば3万円ちょっとの1003HAGと、半年分のb-mobileSIMがマジオススメです!

あと一年分より半年分の方がなぜかお得なので、半年で買いましょう(笑)

2010-04-25

日本MySQLユーザ会(MyNA)会 2010春に行ってきた

| 21:10

実はMySQLよりPostgreSQLが好きなのですが(笑) でも実際使っているのはMySQLが多いですね。。。

非常にゆるい会で、楽しかったです。

内容は

「Drizzleの "いま"」

Drizzleってなんて表現すればいいんだろう?

Drizzle とは、MySQLのもともとの目標である、使いやすさ、信頼性、そしてパフォーマンスに重点を置いたMySQLの派生プロジェクトで、Brian Aker氏(米サン・マイクロシステムズ CTO/Labs:元MySQL Director of Architecture)によって立ち上げられました。

http://www.atmarkit.co.jp/fdb/rensai/drzl_pj/drzl01.html

ってものです。なんかいろいろ派生しているんですが、当分使わないかな。。。そのまえに5.5でしょう(笑)

内容自体は非常に楽しかったです。

「とみたまさひろに訊く『MySQL』」

MyNAの会長のお話!

mysql.gr.jpのドメインを管理したり、MLで文字名を消したりするお仕事だそうです。

って、謙遜だと思いますが、PHP製の某有名Wikiプロジェクトでドメインを管理してた人がドメインの更新メール見落としていて、失効しちゃったことを思い出しました。。。

ドメインの維持って大切ですよね(笑)

上記がメインのセミナーです!

すくない(笑)

最初からビールを飲みながら見ていました。まったりとしていて楽しかったですね。スピーカーの人も酔っているのがさらにいい!

次は是非LTもやって欲しいですね。MySQLに関係ない話とかPostgreSQLとか別のDBの話をLTでやると楽しいかなーって思いました。

あと勉強会とかの運営メンバーが足りないので募集中っていっていました!

ちょっと参加したいなーって思ったのですが、名刺とかまったくもっていなかったのでまた今後の機会にでも(笑)

あと最後は好きな人が発表するセッションで

MySQLのパフォーマンスについて

XtraDBの人がパフォーマンスに関してのセッション。これ非常によかった。普通のセミナーでもなかなか聞けないような内容なんじゃないかな? お酒なしでガッツリと実際に実機触りながら聞きたい内容でした(笑)

チューニングって非常にデリケートですよね。