DreamweaverでXML parsing fatal errorが出た。

DreamWeaverを立ち上げたらこんなエラーが出た。

XML parsing fatal error: An exception occurred!
Type:UnexpectedEOFException, Message:The end of input was not expected, line: 17, file:
C\:Users\xxx\AppData\Roaming\Adobe\Dreamweaver CS4\ja_JP\Configuration\Workspace\Classic.xml

内容を見るに、パネルレイアウトに関するxmlファイルの終わりが予期しない形式になってるよ(要は壊れてるよ)とのことなので、該当のファイルを開いてみると案の定こんな形で途切れてた。

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<panelset>

  <application maximize="false" rect="546 25 1885 1024"/>

  <document maximize="true" rect="133 133 729 305"/>

  <panelframe dock="right" height="891" iconmodewidth="135" width="326" x="1" y="12">
    <panelcontainer expanded="false" height="883" visible="true">
      <panel height="883" id="css styles" unconstrainedheight="300" unconstrainedwidth="208" width="541"/>
      <panel height="883" id="apElements" unconstrainedheight="300" unconstrainedwidth="208" width="541"/>
      <panel height="883" id="tag inspector" unconstrainedheight="300" unconstrainedwidth="208" width="541"/>
    </panelcontainer>
    <panelcontainer expanded="false" height="860" visible="true">
      <panel height="860" id="database" unconstrainedheight="270" unconstrainedwidth="208" width="541"/>
      <panel height="860" id="data sources" unconstrainedheight="270" unconstrainedwidth="208" width="541"/>
      <panel height="860" id="server scripts"

修正のしようもないので、一旦DWを強制終了して該当のファイルを削除してから再起動したら普通に立ち上がった。

あとはパネルレイアウトを再設定して完了。ちなみにClassic.xmlは勝手に再作成された。

山下達郎コンサート

山下達郎コンサート。急遽行かれなくなった友人からチケットを譲り受け、有名どころの何曲かしか知らないままの参加。結果的にはトータル4時間弱の長丁場にもかかわらずそれと感じないほど楽しい時間を過ごせた。

ご本人の歌やおしゃべりは当然として、それを支えるバンドメンバーが素晴らしくてきっとリハーサルの回数以上に息がピタリ。そして山下達郎本人が一人一人のメンバー紹介やソロで活躍する場を作るのを(しかも何度も!)見ていて非常に気持ちが良い。

また、アンコールの後も続けて何曲か歌うところまではお約束だけれど、その”何曲か”が多くて近くのお客さんから「まだやるのか?」という声がちらほらと聞こえて少し可笑しかった。

デビュー37年なんて自分が生まれてもいないような少し気が遠くなる時間。そんなに昔から大舞台のしかも第一線で頑張り続けているなんて本当にすごい。来年で還暦だそうだけれどますますの活躍を願いながらまた聴きに行きたいなと思う。

InnoDBに対してAUTOCOMMITが無効であるのにも関わらずCOMMITするのを忘れていた。

InnoDBに対してAUTOCOMMIT = 0の状態でsqlを発行しCOMMITしなかった(するのを忘れた)場合、次のようになった(idはAUTO_INCREMENT)。

a. スクリプト実行前

id
1 taro
2 jiro
3 saburo

b. スクリプト実行中(shiroを追加するsqlを発行/COMMITせずにスクリプト終了)

id
1 taro
2 jiro
3 saburo
4 shiro

c. スクリプト実行後

id
1 taro
2 jiro
3 saburo

d. 同じsqlを再度発行し、きちんとCOMMITした場合。

id
1 taro
2 jiro
3 saburo
5 shiro

そもそもの原因は、「スクリプト上でsqlを発行してもDBに反映されない”が、同sqlをphpMyAdminなどから直接発行すると正常に反映される」という現象が発生し、ついでに対象となるテーブルのAUTO_INCREMENTに設定したidがなぜか飛び番号になってしまったこと。いろいろと調べた結果、原因は単純にAUTOCOMMITの設定値の問題だった。

MySQLでInnoDBテーブルを利用していた場合、トランザクション時の挙動はAUTOCOMMITの値によって異なる。AUTOCOMMIT = 1の場合は、ユーザーが明示的にmysql_query(“BEGIN”)としなくともトランザクションを自動的に設定してコミットまで行ってくれる。AUTOCOMMIT = 0の場合は、ユーザーがmysql_query(“BEGIN”)とすることが前提となっており、またコミットには同じく明示的にmysql_query(“COMMIT”)が必要となる(もしくはmysql_query(“ROLLBACK”)とすることで反映をキャンセルする)。

参照:13.5.10.2. InnoDB と AUTOCOMMIT

DBの更新については通常であれば次のように常にAUTOCOMMIT = 0として明示的にBEGINおよびCOMMIT/ROLLBACKを行うようにしていたが、今回は例外的に下記を通さずにsqlを個別に発行しなければならない状況になり、かつ、その発行の直前にAUTOCOMMIT = 0を発行していることを忘れていたためにいくらか悩んでしまった。

$dbHandle = mysql_connect(DB_HOST, DB_USER, DB_PASS) or die('Could not connect: ' . mysql_error());
mysql_select_db("TEST");

$sql = "INSERT INTO table_name ...";

mysql_query("SET AUTOCOMMIT = 0");
mysql_query("BEGIN");

try {
	$result = mysql_query($sql);
	if (!$result) {
		throw new Exception('exception : ' . mysql_error()) ;
	}
	mysql_query("COMMIT");

} catch(Exception $e) {
	echo $e->getMessage;
	mysql_query("ROLLBACK");
}

if (!$dbHandle) {
    mysql_close($dbHandle) ;
}

ちなみにAUTOCOMMITの値は下記sqlによって取得する

SELECT @@autocommit;

なお上記のテーブルの履歴で、dにおいて追加されたshiroのidが5となっているのは次の理由からだと思われる。

  • bにおいて発行されたsqlによってidが一つ使用(予約?)される。
  • cにおいてCOMMITなしに接続が切れることによるROLLBACKでレコードは元に戻されるがidは元に戻らない。
  • 結局dではその次のidである5ば使用される。

もし自動コミット モードがオフで、最後のトランザクションを明示的にコミットせずに接続を閉じると、MySQL はそのトランザクションをロールバックします。

参照:13.5.10.9. 暗黙的なトランザクション コミットとロールバック

railsアプリの立ち上げまで簡単にメモ

rvmのインストールに続き、アプリを取り急ぎ立ち上げるまでを簡単にメモ。まずフォルダを作りGemfileを準備する。

$ rvm use 1.9.3
$ mkdir myapp
$ cd myapp
$ vim Gemfile

Gemfileの内容はsourceとrailsのみを記述する。

source "http://rubygems.org/"
gem "rails", "3.2.0"

bundleから各種gemをインストールする。その際に–path vendor/bundleを付けることによって、各アプリ毎にgemを保存する(vendor/bundle以下)。

$ bundle install --path vendor/bundle
Fetching source index for http://rubygems.org/
Installing rake (0.9.2.2)
・・・省略
Installing rails (3.2.0)
Your bundle is complete! It was installed into ./vendor/bundle

railsコマンドを実行。その際にGemfileの上書きに対するアラートが出るが、そのまま実行する。

$ bundle exec rails new .
省略
Overwrite /path/to/book/Gemfile? (enter "h" for help) [Ynaqdh] Y
省略

バージョンによるものなのか不明だが、下記エラーが表示された場合にはtherubyracerが足りないので、上書きしたGemfileにgem ‘therubyracer’を追加して再度bundle install –path vendor/bundleを実行する。

/usr/local/rvm/gems/ruby-1.9.3-p125/gems/bundler-1.0.22/lib/bundler/resolver.rb:280:in `resolve': Could not find gem 'jquery-rails (>= 0) ruby' in any of the gem sources listed in your Gemfile. (Bundler::GemNotFound)

memberテーブルにnameとemailカラムを作成しつつscaffold。

$ bundle exec rails g scaffold member name:string email:string

migrate後にテスト。

$ bundle exec rake db:migrate
$ bundle exec rake test

DBのリセット。

$ bundle exec rake db:migrate:reset
$ bundle exec rake db:seed

サーバーの立ち上げ。

$ bundle exec rails s
=> Booting WEBrick
=> Rails 3.2.0 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2012-02-25 20:53:09] INFO  WEBrick 1.3.1
[2012-02-25 20:53:09] INFO  ruby 1.9.3 (2012-02-16) [i686-linux]
[2012-02-25 20:53:09] INFO  WEBrick::HTTPServer#start: pid=27380 port=3000

ウェブサーバーから下記アドレスにアクセス。

http://192.168.1.72:3000/members/

明日でおしまい

明日で長年勤めさせていただいた社会人学校の講師業が終了する。通常の仕事とは別であったため、主に土曜日に準備にあたり日曜日に教壇に立つという日々で、それが当たり前のように感じるほど。そして、来週からの週休二日生活に一抹の不安を覚える。

学校では二十歳前の学生さんから年配の方まで本当に多くの方に教えさせていただいた。今でこそ授業の組み立てや資料づくりなどをある程度はスムースに出来るようになったけれども、始めた当時を思い返してみるとそれはもう酷いもので、よく嫌にならずについてきてくれたなと思う。当時の生徒さんと時々お会いすることがあって、「先生(私)に教わって良かった」などという有難いお言葉をいただくが、むしろこちらから謝りたい気持ち。

とはいえ、それまで他人にものを教えたことも/教わったこともなく、まして人前で話すことが得意ではなかったこともあって、いろいろと試行錯誤をしながらの授業だった。特に、他人から教わった経験があまりに少ないことが気がかりで、どんな言葉や言い回しが分かりやすいのか、どんな間をとったら良いのか、表情は、声のトーンは、と考えたらきりがない。各種書籍や業界のセミナーやカンファレンス、講演会などに出かけていき、少しずつ勉強をしていった。

そんな中で最も心がけたことは、ある物事を説明する場合にそれを自身が理解できた瞬間を思い出して、その前後のプロセスと共に説明をすること。「理解」の捉え方は人によって千差万別だけれど、それでもそれは物事の抽象度を高めていったその極限、もしくはその過程の一部分であって、かつ演繹が可能な考え方を手に入れることに変わりはないと思う。言葉にしてみれば簡単なことでも、講座の中で生徒さんの考え方をその方向に導いていくことは本当に難しかった。それに、生徒さんの来校の目的によっても方向性を変えなければならないし、そこのところの見極めには非常に悩んだ。ともかく、僕の思うところが少しでも伝わっていたら嬉しいな。

また、講師/生徒としての立場を超えて、個人的に友人としてお付き合いをさせていただいている方も何人かいて、実のところでそんなつながりを生み出してくれる場所が無くなってしまうことが最も寂しい。自分とは異なる年代、考え方を持つ方々とのお話は非常に楽しく、私がむしろ生徒として勉強をさせていただいていたようなものだから。

つらつらと思ったことを文章にしてみた。なかなかうまくまとまらないけれど、最後の準備をしながらこんなことを考えていた。

rvmをsudoを付けて(マルチユーザーとして)インストールする

rvmを管理者としてインストールしてみる(続きrubyも)。こちらのマニュアルのMulti-Userの項目を参考に進める。

$ bash < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)

インストール自体はそれほどかからずに完了する。Single-Userの場合は、それぞれのホームディレクトリ以下に、Multi-Userの場合は/usr/local/rvm以下にインストールされる。また、インストール後は再ログインをする。

Once you have added the users you want to be able to use RVM to the rvm group, those users MUST log out and back in to gain rvm group membership because group memberships are only evaluated by the operating system at initial login time.

また、その後の関連コマンドについては下記のように入力する。sudo rvm -vとするとsudo: rvm: command not foundと怒られる。

$ sudo rvm -v

ではなく

$rvmsudo rvm -v

次にrubyをインストールする。まず、インストールに当たっての必要要件を確認する。

$ rvmsudo rvm requirements
省略

Additional Dependencies:
# For Ruby / Ruby HEAD (MRI, Rubinius, & REE), install the following:
  ruby: /usr/bin/apt-get install build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion

省略

このうち、For Rubyの部分を参考にして足りないライブラリをインストールする。また、rvmsudo rvm list knownによってインストール可能なrubyのバージョン等を確認する。続いて該当バージョンをインストール。

$ rvmsudo rvm list known
$ rvmsudo rvm install 1.9.3
省略
Install of ruby-1.9.3-p125 - #complete

最後にパスを通して、また通常使用するバージョンを指定する。

$ echo '[[ -s "/usr/local/rvm/scripts/rvm" ]] && . "/usr/local/rvm/scripts/rvm" # Load RVM function' >> ~/.bash_profile
$ rvm use 1.9.3 --default
Using /usr/local/rvm/gems/ruby-1.9.3-p125

rubyの削除は次のコマンドで行う

//ruby削除(ソースコードも削除する)
$rvmsudo rvm remove 1.9.3
//ruby削除(ソースコードは削除しない)
$rvmsudo rvm uninstall 1.9.3

マップを読み込んでから印刷ダイアログを自動的に表示する

次の流れで印刷をしたかったが、印刷ダイアログを出力(window.print())するタイミングによってページに含まれるGoogle Mapsが表示されないことがあった。おそらく、window.print()をloadScript()内の最後に書いていたので、非同期で読み込まれるGoogle Mapsの表示を待たずに印刷ダイアログを出力していたのだと思う。

  1. ページの「印刷」ボタンをクリック
  2. 印刷用にページを再構築(データを再度抽出し、Google Mapsを含めてページを作る)
  3. 印刷ダイアログを自動的に表示

そこで、google.maps.event.addListenerを利用してmapがidle状態に変化したら(つまり読み込みが完了したら)印刷ダイアログを表示するように変更したところ期待通りに動いた。当たり前といえば当たり前なんだけど。

サンプルはこちら


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>テスト</title>
</head>
<body>

<script type='text/javascript'>
  function initialize() {
  var myLatlng = new google.maps.LatLng(35.681382, 139.766084);
  var myOptions = {
    zoom: 10,
    center: myLatlng,
    disableDefaultUI: true,
    draggable: false,
    scrollwheel: false,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  }
  var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
  var marker = new google.maps.Marker({
      position: myLatlng,
      map: map,
      title:"Hello World"
  });
  google.maps.event.addListener(map, 'idle', function() {
    alert('load ok');
    window.print();//動く
  });
}

function loadScript() {
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.src = "http://maps.google.com/maps/api/js?sensor=false&callback=initialize";
  document.body.appendChild(script);
  //window.print();//動かない
}

window.onload = loadScript;
</script>

<div id="map_canvas" style="width:500px;height:500px;border:1px solid #666666;"></div>

</body>
</html>

vsftpd 2.3.2でlocal_umask=022がうまく動かない

/etc/vsftpd.confにlocal_umask=0022を設定しても、アップロードしたファイルのパーミッションが0700になってしまう。

# Default umask for local users is 077. You may wish to change this to 022,
# if your users expect that (022 is used by most other ftpd's)
local_umask=0022

umask=0077で0700になるのであれば基準となるパーミションは0777であり、umask=0022に設定すれば0755となると思っていたのだけれど、VSFTPD.CONFのマニュアルによると、ディレクトリ/ファイルが「アップロードで作成された場合」はこの限りではなく、デフォルトは0666である様子。

file_open_mode
The permissions with which uploaded files are created. Umasks are applied on top of this value. You may wish to change to 0777 if you want uploaded files to be executable.
Default: 0666

よって同ファイルにfile_open_mode=0777を追加することで解決した。

# Default umask for local users is 077. You may wish to change this to 022,
# if your users expect that (022 is used by most other ftpd's)
local_umask=0022
file_open_mode=0777

ファイルを「vsftpdを通しながらアップロード以外で作成する」という場面が思いつかず、こうすればとりあえず使える的な内容に。お恥ずかしい限り。

jQuery ui datepickerで入力可能な日付の制限をする

jQuery UIのdatepickerで、入力可能な日付の制限を別に入力された値によって変化させる方法を調べてみた。最初は、選択ボックスの更新時に

$( "#datepicker" ).datepicker({ minDate: '-1m', maxDate: '+1m' });

などと単純に再呼び出しをすれば良いかと思っていたけれど、それでは更新されなかった。そこでマニュアルをよく読んでみると、次の記述の通りsetterのような使い方をしないといけなかった様子(よく読んでいなかっただけ・・・)。

Get or set the maxDate option, after init.
//getter
var maxDate = $( ".selector" ).datepicker( "option", "maxDate" );
//setter
$( ".selector" ).datepicker( "option", "maxDate", '+1m +1w' );

結果、無事に更新が出来るようになった。そのサンプルはこちら。

2012年1月10日追記

onCloseを利用して日付の選択時にチェックを行うように変更した。例えば、土日のみ/ある特定日のみなど。ある一定期間内での入力制限はできても、同じ事を個別にはできなさそうなので、取りあえずはこんなふうにするしかないのかなあ。

また、ソースコードは次の通り。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>日付取得サンプル</title>
<link rel="stylesheet" type="text/css" href="../../common/css/bootstrap.min.css"/>
<link type="text/css" href="./common/css/redmond/jquery-ui-1.8.16.custom.css" rel="stylesheet" />
<script type="text/javascript" src="./common/js/jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="./common/js/jquery-ui-1.8.16.custom.min.js"></script>
<script type="text/javascript">
    $(function(){
		var planid = 0;

        // Datepicker の呼び出し
		$( "#datepicker" ).datepicker({onClose: function(dateText, inst) {
			//プラン番号の取得
			var n = parseInt($( "#changeplan" ).val());

			//日付オブジェクトの作成
			var date = new Date(dateText);
			var dd = parseInt(("0" + (date.getFullYear())).slice(-4) + ("0" + (date.getMonth() + 1)).slice(-2) + ("0" + date.getDate()).slice(-2));
			var wd = parseInt(date.getDay());
			var check = false;

			if(n == 1){
				check = true;
			}else if(n == 2){
				check = true;
			}else if(n == 3){
				check = true;
			}else if(n == 4){
				check = true;
			}else if(n == 5){
				check = true;
			}else if(n == 6){
				check = true;
			}else if(n == 7){
				if(wd == 0 || wd == 6) check = true;
			}else if(n == 8){
				if(wd > 0 && wd < 6) check = true;
			}else if(n == 9){
				if(dd == 20120105) check = true;
			}else{
				check = true;
			}

			if(!check){
				alert("この日程は選択できません");
				$( "#datepicker" ).val('');
			}
		}});

		// Datepickerの初期化
		updatedatepicker(planid);

		// Datepicker の更新(セレクトボックス更新時)
		$( "#changeplan" ).change(function() {
			$( "#datepicker" ).val('');
			updatedatepicker(parseInt($( this ).val()));
		});

		function updatedatepicker(arg){
			var n = arg;
			//オプション作成
			if(n == 1){
				$( "#datepicker" ).datepicker( "option", 'minDate', new Date(2012, 1 - 1, 1) );
				$( "#datepicker" ).datepicker( "option", 'maxDate', new Date(2012, 3 - 1, 31) );
			}else if(n == 2){
				$( "#datepicker" ).datepicker( "option", 'minDate', new Date() );
				$( "#datepicker" ).datepicker( "option", 'maxDate', new Date(2012, 12 - 1, 31) );
			}else if(n == 3){
				$( "#datepicker" ).datepicker( "option", 'minDate', new Date() );
				$( "#datepicker" ).datepicker( "option", 'maxDate', "+1y" );
			}else if(n == 4){
				$( "#datepicker" ).datepicker( "option", 'minDate', new Date() );
				$( "#datepicker" ).datepicker( "option", 'maxDate', "+2m" );
			}else if(n == 5){
				$( "#datepicker" ).datepicker( "option", 'minDate', "-3w" );
				$( "#datepicker" ).datepicker( "option", 'maxDate', "+3w" );
			}else if(n == 6){
				$( "#datepicker" ).datepicker( "option", 'minDate', new Date() );
				$( "#datepicker" ).datepicker( "option", 'maxDate', "+2d" );
			}else if(n == 7){
				//empty
			}else if(n == 8){
				$( "#datepicker" ).datepicker( "option", 'minDate', new Date() );
				$( "#datepicker" ).datepicker( "option", 'maxDate', "+1m" );
			}else if(n == 9){
				$( "#datepicker" ).datepicker( "option", 'minDate', new Date(2012, 1 - 1, 1) );
				$( "#datepicker" ).datepicker( "option", 'maxDate', new Date(2012, 1 - 1, 15) );
			}else{
				//empty
			}
		}

    });
</script>
<style>
table {
	width:560px;
	margin:10px auto;
}

h3 {
	text-align:center;
}
</style>
</head>

<body>
<h3>jQuery ui datepickerで入力可能な日付の制限をする</h3>
<hr />
<table class="bordered-table">
<tbody>
<tr>
<th>プラン選択</th>
<td><select id="changeplan" name="plan">
	<option value="0">選択して下さい</option>
	<option value="1">2012年1月1日から2012年3月31日まで</option>
	<option value="2">本日を起点に2012年12月31日まで</option>
	<option value="3">本日を起点に1年後まで</option>
	<option value="4">本日を起点に2ヶ月後まで</option>
	<option value="5">本日を起点に前後3週間</option>
	<option value="6">本日、明日、明後日</option>
	<option value="7">土曜日と日曜日のみ</option>
	<option value="8">本日を起点に1ヶ月までの平日のみ</option>
	<option value="9">2012年1月5日のみ</option>
</select><br /></td>
</tr>
<tr>
<th>日程選択</th>
<td><input size="12" maxlength="10" id="datepicker" name="date" type="text" value="" /></td>
</tr>
</tbody>
</table>

</body>
</html>