カテゴリー
未分類

電子書籍と紙の本

先日うちに来た友人が「本は電子じゃなくて紙で読みたい」と言っておられましたが、僕の場合は可能であれば技術書は電子書籍で購入しますし、紙の本を買った場合は適当なタイミングで自分でPDF化(自炊)して保存しております。

オライリーの本なんかは、公式サイトからPDF版またはepub版を直接購入することが可能なので、たぶんもうこの出版社の紙の本は買わないだろうなぁと思っとります。

自分の思う電子書籍のメリットは以下の2点。
1.かさばらない&デカくても重くない
2.キーワード検索できる
3.電子書籍形式で販売されていれば、Download & Go ですぐ読める

特に技術書や資料などでは2番のメリットは非常に大きい。

小説や漫画なんかは単語を検索するという機会も少ないと思うので、電子書籍である必要性はあまり無いかもしれませんね。

逆に、電子書籍に足りないものは、「パラパラめくる」という紙の本独自のインターフェイス。
本ってのはついついパラパラめくりたくなるんですが、僕だけでしょうか?

本を読んでいると、もう「めくる」という行為が遺伝子に染みついてるんじゃないかと思うくらいに、本をめくって読みたくなります。

電子書籍であっても「めくる」行為ができないことでフラストレーションを感じることがよくあって、「パラパラめくる」インターフェイスを既存のデバイスでどうにか実現できないかな?
なんて考えちゃうときもあります。

こういうページ検索手法は非論理的だし非効率だけど、すごく本能的な動作のように思えるで、それができないことが電子書籍のデメリットかなぁ。

本をめくるときに指先が覚えている「大体この辺!」っていう感覚と、パラパラとページをはじくスピードに連動して、一瞬だけ目に飛び込む刹那的なページ情報から目的のページを特定する人間の動作を、何とか電子書籍で実現できないもんだろうか。
(誰かエロい人、作って!)

まぁ、あえて不便なインターフェイスを必死になって実装するってどうよ?って気もしますが...。

そのうち時代が流れて電子書籍的な“何か”が一般化した時には、「本をめくる」という行為も過去の遺物となる日が、はたして来るものなのでしょうか。

カテゴリー
SQL

MySQL+Spiderがクラッシュした際の復活方法

MySQL+Spiderの組み合わせで運用している場合、Spiderノードがメモリ不足によりクラッシュする場合がある。
よくある。とくにシステム構築中のテスト稼働や大量データの移行時、パラメータチューニング中など…。

ところがこれが発生すると、通常のrestartやstop~startが効かなくなる。
sockが無いとか、pid fileが腐ってると言われたりして、まったくMySQL起動しなくなる。

そんな時の解決方法として下記手段を用いているが、それでも復活しない場合もある...。

1.まずはsockファイルを作成

vi /tmp/mysql.sock

内容はブランクでよいので、このまま保存してviを閉じる。

2.pidファイルを作成
pidファイルが無くなっているか腐っている場合が多いので、こちらもブランクのファイルを作成。
パスやpidファイル名は適宜変更。
(startする際にpidファイルに関するエラーメッセージが表示されるので、エラーメッセージに表示されるpidファイル名を利用するとよい)

vi /where/you/install/mysql/data/ip-999-999-999-999.pid

3.上記で作成したファイルの権限を変更

chown mysql:mysql /tmp/mysql.sock
chown mysql:mysql /where/you/install/mysql/data/ip-999-999-999-999.pid

4.mysqld_safeを起動させ、その間にmysqldをスタート
ターミナルを2つ用意して、まずはmysql_safeを起動。

/where/you/install/mysql/bin/mysqld_safe

その間に別のターミナルでMySQLサーバを起動します。

service mysql start

これで起動するときがあります。
それでも無理な時はサーバを窓から投げ捨ててください...。

歴戦のDBAとかではないのでこのような対処療法しか知りません。
どなたか、もっと良い方法をご存知の方は是非教えていただきたい。

カテゴリー
cakephp2

Cakephp2.xでモデル内から別のモデルを利用する

Fat modelな書き方をしていると、モデル内から別のモデルを呼び出したい場面が良く出てくる。

モデル内で使いたい別のモデルをimportし、必要な場所でインスタンス化して呼び出せばOK。
下記例はhogeモデル内からfugaモデルを呼び出す準備。

//hoge.phpのクラスに追記
App::import('Model','fuga');
//インスタンス化
$fuga= new Fuga;

//モデルfugaのメソッドを使用できる
$fuga->find('all');

hogeモデル全体で使いたい場合などは先頭でインスタンス化しておくが、その際$this->fugaにモデルインスタンスを格納すれば、hogeモデル内のどのメソッドからも$this->fuga->find(‘all’);できる。
まぁあたりまえっちゃぁ、あたりまえか。

カテゴリー
Sublimetext2

Sublimetext2のショートカットまとめ

Sublimetext3のリリースが待ち遠しいのですが、sublimetext2でよく使うけど忘れがちなデフォルトのショートカットまとめ。
(Winユーザーなので、windowsでのショートカットです)
まぁ、preference見ろという話ではありますが…。

コンソールの表示

Ctrl+@

コマンドパレット

Ctrl+shift+p

シンボル一覧(クラスや関数定義の一覧)

Ctrl+r

検索ボックス

Ctrl+Shift+f
※ただのCtrl+fだと操作中のファイルのみを対象とした検索用オプションしか表示されません。

検索置換ボックス

Ctrl+h

ペイン分割

Alt+Shift+[1~4の画面を分割したい数字]

プロジェクトの切替

Ctrl+Alt+p

他に出てきたら、また追記します。

カテゴリー
未分類

TeraTermのログイン用マクロ

TeraTerm使ってますかー?

いっぱいログイン先のサーバがある場合とか、いちいちログインダイアログに入力するのめんどくさいですよねー。
という人(私)向けの、ログインマクロをご紹介。

まずはパスワードでログインする場合

connect '192.168.0.0:22 /ssh /auth=password /passwd=your_password /user=login_account /F=theme_file.ini'

次にキーファイルを使ってログインする場合

connect '192.168.0.0:22 /ssh /2 /auth=publickey /user=login_account /keyfile=public_key_file.pem /F=theme_file.ini'

どちらも最後のパラメータF=はカラーテーマのファイルがある場合は指定してください。
指定しなければデフォルトカラーでログインします。
上記いずれかの内容を、ログイン先やアカウント、パスワードなどを適宜変更の上、拡張子をttlにしてTeraTermのexeファイルがあるディレクトリに保存しましょう。

AmazonのEC2などを利用していて、大量にサーバを抱える場合は重宝しますよ!

カテゴリー
cakephp

Cakephp2のAuthコンポーネントで複数ログイン機能を制御

CakePHP2による開発で、何種類かのログイン機能を一つのウェブサービスに実装したい場合のお話し。

たとえば管理者用機能と一般ユーザーでのログイン状態の切り分けや、一つのサイト内で別のサービスを立ち上げて2種類以上のユーザーを抱えるような場合、aclによる制御も一つの方法ですが、そこまで厳密にユーザー権限を管理する必要は無いケースも多々あるはず。

単純にユーザーテーブルとloginメソッド実装したコントローラーを複数用意して、「ログイン出来たー!」と思ったらログイン情報が錯綜してしまい、あっちの機能でログインしたら、こっちの機能のログイン状態がおかしくなった...。
という場合の対処方法。

簡単な話で、各コントローラごとにログインを管理するグループに合わせてAuthComponentのセッションキーを変えればOK。

たとえば管理者機能(Adminグループ)のログインと、一般ユーザー(Userグループ)でのログイン機能の2種類がある場合、Authコンポーネントで用いるユーザー用テーブルを2種類用意する。

#管理者ユーザーテーブル
CREATE TABLE `admins` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  `password` varchar(45) DEFAULT NULL,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

#一般ユーザーテーブル
CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  `password` varchar(45) DEFAULT NULL,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

当然対応するAdminModelとUserModelは作成するとして、コントローラも2種類(管理者用機能と一般ユーザー用機能)を用意する必要がある。

各コントローラでAuthコンポーネントを設定する時に、

  • ログインに使用するモデルをグループに合わせて指定
  • beforeFilterで各グループごとに個別のsessionKeyを指定

これだけで、狙ったユーザーグループを用いたログイン機能が実現できる。

//AdminsController
public $components = array(
	'Auth' => array(
		'authenticate' => array(
			'Form' => array(
				'fields' => array(
					'username' => 'name',
					'password' => 'password',
				),
				'userModel' => 'Admin',
			),
		),
	),
);
public function beforeFilter() {
	parent::beforeFilter();
	AuthComponent::$sessionKey = 'Auth.admins';
}
//UsersController
public $components = array(
	'Auth' => array(
		'authenticate' => array(
			'Form' => array(
				'fields' => array(
					'username' => 'name',
					'password' => 'password',
				),
				'userModel' => 'User',
			),
		),
	),
);
public function beforeFilter() {
	parent::beforeFilter();
	AuthComponent::$sessionKey = 'Auth.users';
}

こんな感じで設定しておけば、あとは通常のCakePHPアプリのようにloginアクションやふつうのアクションを定義していけば、2種類のログイン情報が錯綜することなく処理されます。

コントローラを増やす場合は、各コントローラで上記の設定を行う必要がある点を忘れないように注意!

カテゴリー
PHP

PHPで形態素解析

サイト内検索を実装する際、サーバの都合でMecabをインストールできなかったりする場合もある。
そんな時に重宝するのがPHPでできた形態素解析器「Igo-php」です。

元々はJavaで作られていたMecab互換を目指した形態素解析器で、それがさらにPHPに移植されたというもの。

PHP版には辞書ファイルを作成する機能が無いようなので、Mecabの辞書をダウンロードしてJava版のIgoで辞書ファイルをコンパイルする必要があります。

参考にしたのはこちらのサイト

カテゴリー
CSS

サイドバーの下部がサイトコンテンツの下部とそろわない場合

いろいろ対処方法があるのは知ってるよ。
でもね、古い作りのサイトとか、ぶっちゃけ場当たり的な対処をやっつけでやんなくちゃいけない場合ってのもある。

一番手っ取り早いのは、やっぱり背景レイヤーとなっている要素のサイドバー部分(右端とか左端)にbackground-imageを割り当てて、repeat-yするのが簡単かつ確実だったりするんだ。
固定幅のサイドバーでfloatしてたり、position:absoluteなんかだったら特にね!

カテゴリー
pukiwiki

pukiwiki plus! のdumpプラグインでリストアが動作しない!

なぜか、pukiwiki plus! のdumpプラグインでリストアが機能しない現象にあたりました。
そもそもpukiwikiに詳しくないため、dumpプラグインのソースを解析することになったのでその顛末を書きとめておきます。

そもそもリストアのプラグインが呼ばれないじゃないか!

dumpプラグインではリストアするファイルをPOSTしたフォームにMenuというパラメータが無ければリストアのアクションが起動しないようでした。

// メニューを表示する必要があるか?
if (! isset($vars['menu'])) {
	// 入力フォームを表示
	$body = plugin_dump_disp_form();
	return array('msg' => $msg, 'body' => $body);
}

このように$vars[‘menu’]が要求され、なければ74行目のreturnでプラグインが終了してしまいます。
にもかかわらず、これをPOSTするフォームはというと…

  <input type="hidden" name="cmd"  value="dump" />
  <input type="hidden" name="page" value="$defaultpage" />
  <input type="hidden" name="act"  value="$act_up" />

となっており、menuの値など一切渡されません。

そこで、フォームを変更してmenuに’1’をセットして渡すようにしました。

  <input type="hidden" name="cmd"  value="dump" />
  <input type="hidden" name="page" value="$defaultpage" />
  <input type="hidden" name="act"  value="$act_up" />
  <input type="hidden" name="menu" value="1" />

これでひとまず、リストア処理が呼び出されるところまで行きましたが…
なぜか「解凍するファイルが無い!」というエラーが発生します。

よくよく調べていくと、アップロードするtar.gzからファイルを取り出す処理でなにやらフィルターが…

if ($name == TARLIB_DATA_LONGLINK) {
  // LongLink
  $buff     = fread($this->fp, $pdsz);
  $longname = substr($buff, 0, $size);
} else if (preg_match("/$pattern/", $name) ) {
  $buff = fread($this->fp, $pdsz);
  // 既に同じファイルがある場合は上書きされる
  $fpw = @fopen($name, 'wb');
  if ($fpw !== FALSE) {
    flock($fpw, LOCK_EX);
    fwrite($fpw, $buff, $size);
    @chmod($name, 0666);
    @touch($name, $mtime);
    flock($fpw, LOCK_UN);

この752行目のpreg_matchのパターンが怪しい…

どうやらこのパターン、復元先を保証するために設定されているようで、
ダンプファイルを取得したディレクトリ構造と同じ位置に復元する
という仕様のようでした。

私が今回試していたのは、実は別のWikiサービスからとってきたDumpを自分のサーバに上げようとしていたため、取得したダンプファイルのディレクトリ構造とリストア先のサーバのディレクトリ構造が異なっていたため、このフィルタに引っ掛かって上手くリストアされていなかったようです。

うーん、Dumpプラグインを改造しなくちゃです。

カテゴリー
SQL

ツリーの閉包モデル リーフの移動、追加で自分への補足 「SQLアンチパターン」より

1月にオライリー・ジャパンより「SQLアンチパターン」が発売されました。
この本で紹介されているツリー構造をRDBMSで扱う手法の一つ「閉包モデル」は初めて知ったのですが、最後の方に出てくるpath_lengthを追加した場合のSQLが掲載されていなかったので、自分のために補足として書き残しておきます。

以下は本に掲載されているリーフを追加する際のSQLですが…

INSERT INTO TreePaths (ancestor, descendant)
SELECT t.ancestor, 8
FROM TreePaths AS t
WHERE t.descendant = 5
UNION ALL
SELECT 8, 8;

このままではpath_lengthが追加されません。
そこで、以下のようにサブクエリ部分のフィールドで既存レコードのpath_lengthに+1して返すようにすれば、追加されるリーフのpath_lengthは自動で計算されることになります。

INSERT INTO TreePaths (ancestor, descendant, path_length)
SELECT t.ancestor, t.path_length + 1, 8
FROM TreePaths AS t
WHERE t.descendant = 5
UNION ALL
SELECT 8, 8;

また、リーフを移動する際のSQLは以下のように掲載されていますが…

INSERT INTO TreePaths (ancestor, descendant)
SELECT supertree.ancestor, subtree.descendant
FROM TreePaths AS supertree
CROSS JOIN TreePaths AS subtree
WHERE supertree.descendant = 3
AND subtree.ancestor = 6;

これを以下のようにサブクエリ部分のフィールドでsupertree.path_length + subtree.path_length + 1を返せば、自動的にpath_lengthが計算された状態でリーフが移動されます。

INSERT INTO TreePaths (ancestor, descendant,path_length)
SELECT supertree.ancestor, subtree.descendant, supertree.path_length + subtree.path_length + 1
FROM TreePaths AS supertree
CROSS JOIN TreePaths AS subtree
WHERE supertree.descendant = 3
AND subtree.ancestor = 6;

以上、試してないけど、たぶんこれでいけるはず。