PostgreSQLのクエリプランナ/クエリオプティマイザは非常に優秀です。ユーザーはただ自分が欲しいデータをSQLで記述し、酷使するカラムに対してインデックスを張ってやればいいだけです(DB自体が酷使されてるなら細かいチューニングは必須です)。MySQLはそれら一連の最適化を人間が担当することになります。
後で詳しく書きますが、たとえば、"SELECT * FROM foo WHERE id IN (1,2)"と"SELECT * FROM foo WHERE id=1 OR id=2"の2つのクエリを、PostgreSQLはまったく同じように処理します。意味が同じなので当然といえば当然です。MySQLもたぶんここまでは同じです。問題は次。
aidとbidとcidの3カラムが登場しました。どちらのクエリも意味は同じです。さて、aid,bid,cidともにインデックスが張られているとして、MySQLでは後者のほうが圧倒的に高速です(コメント欄1つめ)。PostgreSQLではどちらも同じか、2つ目のごちゃごちゃしたやつのほうが遅いかだと思います。ちなみに、2つ目のSQLにおいてcidのインデックスは使われません。
本稿では、このようなMySQLの癖についてとりあえずまとめたいと思います。PostgreSQLは8.2以降、MySQLは5.1を想定しています。
たとえaid,bid,cidにそれぞれインデックスが張られていようとも、SELECT (略) WHERE aid=1 OR bid=2 ORDER BY cid DESCという一文において使われるのはひとつだけです。aid=1を探すのにインデックスを使うと、bidの絞り込みは全スキャン(MySQLのEXPLAINでいうとALL)、cidでのソートもまた全スキャン(同Filesort)になります。こういうクエリをEXPLAINすると、負け惜しみのようにpossible_keysとかいって候補一覧を出してくるところが苛立たしいです。わかってんなら使えよ。
この欠点というか怠惰なところに対処したのが、さっきのUNIONとか出てきたほうのクエリです。UNIONの前後でそれぞれ独立した文なのでインデックスを計2回使えます。UNIONで動的に作ったテーブルなのでfooに張られたcidのインデックスは出番がありません(Filesortになります)。いちおう次のようにすることで、すべてインデックスを使うクエリにできます。
UNIONの結果集合(WHEREで絞り込んだ部分集合)をテンポラリテーブルにして、cidにインデックスを張っていったん終わり、次のクエリで改めてORDER BYして取り出す形です。tmpの集合をこれ以外の用途(たとえばSELECT count(*)するとか)にも使う予定があるなら、テンポラリテーブルにして使いまわしたほうがいいと思います。
さて。ORがUNION(和)ならANDはINTERSECT(積)だな、と思うかもしれませんが、MySQLはINTERSECTに対応してません。このようにサブクエリをJOINしまくることで一応実現はできますが、インデックスは不完全燃焼です。ORのときのようにテンポラリテーブルを作ってもいいですが、一般的にはインデックスの張り方を工夫して対処します。
たとえばWHERE did=5 AND eid=6という条件式であれば、didとeidの複合キーを作ります。ここでWHERE eid=6 AND did=5と書くとdid_eidなインデックスは使われないので注意です。意味は同じですが解釈が変わり、MySQLは黙々と全スキャンし始めます。
O/Rマッパのようなものを使っているのならWHERE句になるべきものは配列か何かで保持しているはずなので、SQLへ変換する前に辞書順でソートするようにしておけば順番については気にしなくてもいいかもしれません。
しかし調子に乗ってaid,bid,aid_bid,cid,did_eidなどなど片っ端からインデックスを張るのは美学的にも実用的にもおすすめできません。実テーブルの数倍の容量を持つインデックスがいい仕事してくれるときもありますが、なんというか、効果も感想も微妙です。
言いたいことはここで全部言われてますが、補足すると、DATETIME型のカラムをBETWEENしたいときは+ INTERVAL 1 DAYとかするといいらしいです(未検証)。たぶんdtが文字列系の何かにキャストされて張られたインデックスがはがれ落ちるのを防ぐ意味があるんだと思う。ていうかそもそもDATE型の範囲が1000-01-01から9999-12-31ってとこからして、内部的には中途半端に文字列として扱われてるような気もする。
あ、PostgreSQL使いに向けての説明だってことを思い出したので書いておきます。CREATE TABLE foo ( dt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP)は使えません。CREATE TABLE時にCURRENT_TIMESTAMPが解釈され、テーブルを作った時刻がデフォルト値になります。INSERT時に毎回CURRENT_TIMESTAMPを指定してください。それを自動でやってくれるのがTIMESTAMP型とのことですが、まったく関係ないUPDATEが走っても勝手にカラムの値が更新されます。つまり、updated_atには使えるけどcreated_atには使えないということです。
この微妙な状況を打開するため、日付情報を整数型として使う手が出てきます。20090615みたいなやつですね。秒まで記録するにはINTじゃ桁が足りないのでBIGINTを使って20090615232425のようにします。
こいつにインデックスを張り、仮にカラム名をtsとすると、ts >= 20090615000000 AND ts < 20090616000000とすることでインデックスを使いつつ特定日時・時刻の結果が得られます。ts LIKE '20090615%'のように手抜きしないこと。tsが文字列になってインデックスが使われなくなりますので。FLOOR(ts / 1000000) = 20090615も同様にダメです。
「RDBMSはJOINが遅いから〜」という言説を聞いたことがあるかと思います。これも上で述べた「1 SELECTで使われるインデックスは1つ」がその理由です。ていうかRDBMSじゃなくてMySQL特有の現象だと思います。
JOINをなるべく控えるのは、もちろん「1 SELECTで使われるインデックスは1つ」の法則により、貴重なインデックス使用枠がひとつ減るからです。逆にサブクエリは、インデックスの使用枠をひとつ増やせるので積極的に使っていきましょう。
PostgreSQLのような賢いクエリプランナが居ないことを思い出してください。WHERE posts.writer_id IN (SELECT id FROM writer WHERE name LIKE 'john')と書けば勝手にWHERE EXISTS(SELECT 1 FROM writer WHERE name LIKE 'john' AND id=posts.writer_id)と同じ意味だと理解してよしなにやってくれますが、MySQLでは後者のほうが速いです。サブクエリの中のSELECTも*やidなどではなく1などの定数を使ったほうが若干速いようです。
ORDER BY foo DESC LIMIT 10と書けば上から10件見つかった時点で処理を停止するので、LIMIT 100やLIMITなしよりも速いはずだと思いがちですが、別にそういうわけでもありません(細かいことをいえばLIMIT 1よりLIMIT 1000000のほうが遅いに決まってますが、フェッチの時間を無視すれば誤差です)。例外はLIMIT 0で、これはクエリの解析を終えた時点で終了するので(実データを見にいかないので)高速です。文法チェッカーとしてLIMIT 0を使いましょう、みたいなことをMySQLのサイトで見た気がします。
PostgreSQLはたしか8.2か8.3あたりでORDER BY 〜 LIMIT構文の最適化がなされて、LIMIT 10ならまず上位10件を特定し、その10件だけをソートすることで高速化してる(つまりLIMITしたあとORDER BYしてる)のでLIMITの数はけっこう影響しますが、MySQLは全部ORDER BYしたあと改めてLIMITなので、1でも1000でもそんなに差はないです。
MySQLはGROUP BYやcount(),sum()などの集約関数が遅いと言われていたので身構えてましたが、いろいろ触るうちに言うほど遅くないなと思うようになりました。集約関数よりも、インデックスがひとつしか使われない影響の方が大です。JOINがインデックス枠をひとつ消費するようにGROUP BYもひとつ消費するので、それに気をつけていれば大丈夫かと。
MySQLのGROUP BYはPostgreSQLのような厳格さがなくて個人的には使いやすいです。
とりあえず第一部はこれで終了ということで。
まとめ:そりゃみんなRDBMSよりDHTに興味持つわ
]]>http://github.com/tt25/tinychat/blob/a0aa1b01105a66f5c117278c0b1ed1aa8f312d32/index.rb
元ネタの1/4くらい。思ったより短くならなかった。
データベースがTDBなのでやろうと思えば簡単なログ検索もつけれるけどパス。
]]>githubのソースを参考に作ったのに引数の数がおかしいとか言われたので*argsで逃げた。
# start.rb require "rubygems" require "sinatra" require "erubis" module Sinatra::Templates def erubis(*args) render :erubis,*args end def render_erubis(*args) erubis=Erubis::Eruby.new(File.read("views/#{args.first}.erubis")) erubis.result(binding()) end end get "/" do @test="a<h1>h1</h1>a" erubis :index,:layout => "wrap" end
views/index.erubis
<%= @test %> <%== @test %>
views/wrap.erubis
layout start <hr /> <%= yield %> <hr /> layout end
なんか細かいところが気になるけど上手く動いたからまあいいや。
Sinatraのバージョンは0.9.1.1で試したけど将来APIとかが変わる可能性は高いと思うのでご注意。
それにしてもMerbのルーターとRailsのモテっぷり(webratやcucumberが普通に使える)とSinatraの潔さとRamazeの貪欲さを備えたフレームワークはないんでしょうか。
]]>TwitterFoxの話ですが普段はP3:PeraPeraPrvを常用してます。いろいろあってTwitterFoxも使うことになったので作業したメモです。
ここでは現時点で最新のTwitterFox 1.7.7.1をベースに話を進めますが、たぶん近いバージョンなら同じだと思います。もちろん本家が更新されれば同じ手順の繰り返しなので微妙な解決手段ですけども、僕の用途だとそれでもまあ大丈夫なのでいちおう書きます。バージョン追従が面倒になれば他の手を探せばいいし、みたいな。
当然ながら無保証ですし、Firefoxアドオンの自動更新の仕組みすらよくわかってないまま書いてます。
twitterfox-1.7.7.1-fx.xpiはただのzipファイルなので、拡張子を変えていつもの方法で解凍します。いくつかのファイルとフォルダが出てくるかと思います。
上のほう(7行目)に、
<em:name>TwitterFox</em:name>
というのがあるので
<em:name>TwitterFox Patched</em:name>
とでもしておきましょう。これがアドオンリストに出てくる名前になります。
ただのzipファイルなので、拡張子を変えていつもの方法で解凍します。contentとlocaleができるはず。
http://gist.github.com/97675にパッチを置いてますが、変更点はちょっとなので手でやってもいいです。
大雑把に説明すると、赤いマークの行を消して緑のマークの行をコピペで同じ場所に追加すればOKです。ファイル名っぽいものとかグレーのところは無視してください。
普通にZIPにすればOKですが、ディレクトリ構造に注意してください。古いほうのTwitterFox.jarファイルは消すなりリネームするなりして退避しておきます。
手順5と同じような感じです。ディレクトリ構造に注意。
ここでは仮にtw.xpiとします。
オリジナルのTwitterFoxを削除して、新しくTwitterFox Patchedをインストール。Firefoxを再起動すれば終わりです。アカウント情報とかも引き継げているはず。
]]>とりあえず/home/user/rubyにRubyをインストールする前提で。
# wget http://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz # tar xvf ruby-1.8.7-p72.tar.gz # sudo apt-get build-dep ruby -- Rubyのビルドに必要なパッケージがわらわらとインストールされる -- # cd ruby-1.8.7 ruby-1.8.7# ./configure --prefix=/home/user/ruby ruby-1.8.7# make ruby-1.8.7# make test ruby-1.8.7# make install # ~/ruby/bin/ruby -v ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
これでインストール完了。続いてPATHを通す。
# vim ~/.bashrc export PATH=/home/user/ruby/bin:$PATH alias sudo="sudo env PATH=$PATH" # セキュリティの都合でsudoがPATHを引き継がないのでこれで対応 # source ~/.bashrc # ruby -v ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
sudo ruby setup.rbのsudoは要らないかもしれない。
# wget http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz # tar xvf rubygems-1.3.1.tgz # cd rubygems-1.3.1 rubygems-1.3.1# sudo ruby setup.rb rubygems-1.3.1# gem -v 1.3.1 # sudo gem i rack no such file to load -- zlib (LoadError)
zlibがないと言われるので以下のようにする。ここもやっぱりsudoじゃなくていい気がする。
# sudo apt-get install zlib1g-dev # cd ~/ruby-1.8.7-p72/ext/zlib ~/ruby-1.8.7-p72/ext/zlib# ruby extconf.rb ~/ruby-1.8.7-p72/ext/zlib# make ~/ruby-1.8.7-p72/ext/zlib# sudo make install # sudo gem i rack Successfully installed rack-0.9.1 1 gem installed
ちゃんと入った。
普通にapt-getから。
# sudo apt-get install subversion
これも普通にapt-getから。
# sudo apt-get install libsqlite3-dev
gemも忘れずに。
# sudo gem i sqlite3-ruby
Redmine 0.8がRails 2.1.2を要求してくるので入れる。
# sudo gem i rails -v=2.1.2
普通にgemで。
# sudo gem i rake
Redmineがopensslないぞって怒るので入れる。
# sudo apt-get install libssl-dev # cd ~/ruby-1.8.7-p72/ext/openssl ~/ruby-1.8.7-p72/ext/openssl# ruby extconf.rb ~/ruby-1.8.7-p72/ext/openssl# make ~/ruby-1.8.7-p72/ext/openssl# make install
Redmineのダウンロードページを参考に。ここでは0.8ブランチからチェックアウト。
# svn co http://redmine.rubyforge.org/svn/branches/0.8-stable redmine-0.8 # cd redmine-0.8 redmine-0.8# mv config/database.yml{.sample,} redmine-0.8# vim config/database.yml productionのadapterをsqlite3へ変更 redmine-0.8# rake db:migrate RAILS_ENV=production -- データベースつくる -- redmine-0.8# rake redmine:load_default_data RAILS_ENV=production redmine-0.8# ruby ./script/server RAILS_ENV=production => Booting WEBrick... => Rails 2.1.2 application started on http://0.0.0.0:3000 => Ctrl-C to shutdown server; call with --help for options [2009-03-07 17:26:29] INFO WEBrick 1.3.1 [2009-03-07 17:26:29] INFO ruby 1.8.7 (2008-08-11) [x86_64-linux] [2009-03-07 17:26:29] INFO WEBrick::HTTPServer#start: pid=14527 port=3000
これで終わり。あとはApache+Passengerするなりthinやebbでサーバ立てるなりお好みで。
]]>が面白い使い方だなあと思ったのでつくった。pure javascriptなのでソースを一式ダウンロードしたあとindex.htmlを開けば普通に動きます。
http://tt25.org/static/googles_eye/
addEventListenerの分岐が面倒だったのでIEでは動きません。FirefoxとOperaで動いてるのを確認しましたが、Firefoxのほうが軽快に動くっぽいです。
それにしてもイギリスオワタとかアメリカオワタとかいろんな国が終了してますね。
]]>メインブラウザはOperaで、Firefoxはニコニコ動画とか開発とかにしか使わないので普通のバイナリとの速度差はよくわからないけど、レンダリングはやたら速くなったような気がする。
あとgcc -march=nativeで手元のPC(CPU)に最適化されるということを知った。
foo22てのはmozconfigで指定してるディレクトリ。普通はもっとまともな名前を付ける。
ccache付けたら速いとどっかに書いてたので付けたけど、速くなるのは2回目以降のビルド時間であってバイナリにはまったく関係ないということがあとでわかった。
http://gist.github.com/raw/72769/36058bd2c618d9662394ab74e32fd0dfb6c639ec/mozconfig
about:buildconfigとかのメモ。
sudo apt-get install latex-extra-jaでたぶん必要パッケージが全部取ってこれるはず。Windowsはぐぐれ。
関係ないけど未知のものをaptでインストールする場合、なるべく瑣末そうなパッケージをインストールすると依存関係の都合で必要なものがほぼすべて揃うのでオススメ。必要ないものが入る可能性も高いけど、変なところでつまづくリスクとトレードオフ。
よくわからん。shとbashのようなもの?
% ここはコメント \foo % fooマクロだかfooコマンドだかを呼び出し \bar{ あああ } % barマクロだかコマンドだかに「あああ」を渡して呼び出し \baz[opt]{ いいい } % bazマクロだかコマンドだかにオプション「opt」、引数?「いいい」を渡して呼び出し \def\hoge{} %hogeマクロだかコマンドだかを定義 % TeX組み込みの\titleを使って文書のタイトルを設定 % この定義はPDFにも持ち越される。 % \authorとか\dateも同様。 \title{この文書のタイトル} \author{tt25} \date{\today} % \todayでその日の日付 % documentclassマクロだかコマンドだかを、オプション「a4paper,notitlepage」、引数?「book」で呼び出す。 \documentclass[a4paper,notitlepage]{book} \begin{document} ここに本文が入る。 \end{document}
foo.texをfoo.pdfにする場合。文字コードはEUCが無難ぽい。Windows環境ならSJISが無難ぽい。
platex foo && dvipdfmx foo
dvipdfmxのコマンドライン引数で用紙サイズやらを指定する。
% font.map % 使用フォントは.texファイルと同じディレクトリに置いとけばいいっぽい。 rml H :0:ipam.ttf gbm H :0:ipag.ttf rmlv V :0:ipam.ttf gbmv V :0:ipag.ttf # font.mapの定義を参考にA5サイズのbar.pdfが生成され、フォントが埋め込まれる platex foo && dvipdfmx -p a5 -f font.map -o bar.pdf foo
\documentclass{tbook}
で縦書き書籍用の書式が使える。bookだと横書き。treportで縦書きのレポートなど。
既存クラス(tbookとか)を下地に自分でクラスも作れる。
% よくわからん \NeedsTeXFormat{pLaTeX2e} % 名前。\documentclass{mytbook}で呼び出す。 \ProvidesClass{mytbook} % ベースクラスとオプション \LoadClass[a5paper]{tbook} % 使うパッケージ(requireみたいなもん) \usepackage{wallpaper} \usepackage{color}
よくわからん。使うぶんにはあんま区別する必要なさそう。
改行2回で段落区切り(HTMLでいう<p>)。「\\」で強制改行(HTMLでいう<br />)。
あああ いいい\\ううう えええ おおお % HTMLでいうと % <p>あああ</p><p>いいい<br />ううう % えええ</p><p>おおお</p>
\parはよくわからんけど、\par{いえーい}で<p>いえーい</p>相当だと思う。
\parboxはさらによくわからん。\parboxのオプションはTeXとLaTeXでがらっと違う。
Ruby文法の擬似コードでいうとたぶんだいたいこういうマクロ。heightが必須。
def parbox align=t,width=0pt,valign=nil,height,text # t = top # c = center # b = bottom valign ||= align box=Box.new({:width => width,:height => height}) box.insert(text) end
こういう感じで使う。使いどころがよくわからない。
\parbox[t][10mm][c]{50mm}{ ここは幅10mm高さ50mmのparbox。 }
\fboxをHTMLでいうdivのように使う。divなので中には何でも入れれる(っぽい)。
\fbox{\hbox{hboxが枠つきで表示される}}
\hfilとか\vfilを使う。無限長のパディングらしい。無限長のパディングで文字列をはさむと中央になるらしい。
\hfil中央寄せしたい文字列\hfil \hfil右寄せ 左寄せ\hfil
\vfil \hfil これが天地中央に表示されると思いきや、たぶんhboxとかを併用しないとちゃんと天地中央に寄ってくれない。 \hfil \vfil
% 以下の定義をRubyでいうとこう。Rubyだと#はコメントだけど、ここでは気にしないこと。 % def mytitlepage #1=nil,#2 % ..... % end % Usage: % \mytitlepage{タイトル} % \mytitlepage[image.eps]{扉絵とかのページ} \newcommand{\mytitlepage}[2][\null]{% 引数2個、1個目のデフォルト値は"\null" \ifx#1\null % #1と\nullが一致するか。Rubyでいうif #1 == null {\LARGE #2} \else \includegraphics[height=\paperwidth]{#1} \fi }
あと\renewcommandでコマンドを上書き定義できる。
\def\foo{ do something.. }って感じみたい。呼び出すときは\foo。
\def\foo{% \hbox{bar} } \foo % 「\hbox{bar}」と同義
よくわからない。マクロ定義で代用してる。
\def\danger{赤色} \def\safe{緑色} 自爆装置のランプを確認してください。 色が「\safe」に点灯していれば待機中ですが、「\danger」である場合はもうすぐ自爆します。
| mm | ミリメートル |
| pt | ポイント |
| in | インチ |
| zw | 全角1文字の幅 |
| zh | 全角1文字の高さ |
だと思う。
CSSでいうbody{margin:0;padding:0;}みたいなの。出版用語でいう小口とかのどとかいうやつ。
\textheight=116mm \textwidth=130mm \topmargin=10mm % 上、あるいは天のマージン \headsep=20mm % ヘッダと本文の隙間 \footskip=30mm % 本文とフッタの隙間 \oddsidemargin =-1in % 文字通り偶数ページの横のマージン。 \evensidemargin=-1in % 同じく奇数ページ。 \parindent=0pt % 本文のインデント幅。CSSでいうtext-indent \renewcommand{\baselinestretch}{1.6} % 行間。CSSでいうline-heightで単位はemかな。
どっちも別ファイル(.tex)をロードする。\input{ other.tex }みたいにして使う。
\includeは改ページする(条件不明ながら空白のページができたりもする)。\inputは改ページしない(前のページとつながることもある)。
よくわからん。名前からしてボックスが水平か垂直かだと思う。何がだ。
たぶん\hbox to 10mm{}で10mmの「幅」を持つボックス、\vbox to 10mm{}で10mmの「高さ」を持つボックスがそれぞれできるはず。たぶん。
\[hv]boxと同様、水平か垂直の方向にスペースを挿入する。
行頭にあると無視されるので、そういうときは\vspace*{10mm}とか「*」を付けて呼び出す。
よくわからないけどバッファがどうとかっぽい。
\small{ \input{huge.tex} } % huge.texが大きすぎるとエラー出る \small \input{huge.tex} % これならOK。でも後続のテキスト全部にsmallが適用されるので注意。
\small{}はブロック内だけの定義、\smallはグローバルなフォントサイズがsmallに設定される、みたいな感じかな。
Firefoxで<img src="notfound.png" alt="awesome image" />を表示したときみたいになる(枠付きのボックスで挿入予定のファイル名が表示される)。
draftじゃなくfinalにするか、単にdraftをけずればOK。
\def\ps@PlainPage{ \def\@oddhead{} \def\@oddfoot{% \hfil\thepage\hfil }% \def\@evenhead{} \def\@evenfoot{% \hfil\thepage\hfil }% }% \pagestyle{PlainPage}
って感じ。\ps@が何かは知らない。
\thiscmdは改ページされると消滅する揮発性の定義。\cmdはもっとグローバル。
thisっていうくらいなので、たぶんページの中から呼び出さないとダメな気がする。
\def\tume{\leavevmode\kern-.5zw} \tume 「今ならなんと2000円」と社長は言った。
\leavevmodeとか\kernが何かは知らない。-.5zwは「0.5zw分マイナスせよ」の意味だと思う。処理内容をHTML+CSSでいうと、
<style type="text/css"> p.tume { text-indent:-0.5em} </style> <p class="tume">「今ならなんと2000円」と社長は言った。</p>
くらいの意味合い。
colorパッケージが必要。\hrulefillで普通の線(HTMLでいうと<hr />)。
\usepackage{color} \color{red}{\hrulefill} % 赤い水平線が表示されるはず。]]>
を見つけたので気分転換に訳した。
The first 90 percent of the code accounts for the first 90 percent of the development time…The remaining 10 percent of the code accounts for the other 90 percent of the development time.
コードの最初の90%は、開発期間の最初の90%のためにある。コードの残りの10%は、他の開発期間の90%のためにある。
Tom Cargill
Most of you are familiar with the virtues of a programmer. There are three, of course: laziness, impatience, and hubris.
プログラマにとって馴染み深い美徳が3つある。言うまでもなく、怠惰、短気、傲慢のことだ。
(有名なあれ。元ネタは七つの大罪でLarryはクリスチャンです。お見知りおきを)
Larry Wall
Measuring programming progress by lines of code is like measuring aircraft building progress by weight.
プログラミングの進捗をコードの行数で測るのは、飛行機製造の進捗を重さで測るようなものだ。
ビルゲイツ
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.
デバッグはコードを書き始めるよりも2倍難しい。ゆえに、これ以上ないほど巧妙にコードを書いたとしても、定義により、デバッグするには充分ではない。
Once a new technology starts rolling, if you’re not part of the steamroller, you’re part of the road.
新しいテクノロジが回転し始めるとき、もし君がロードローラーの側に居ないなら道路の一部になる。(あんまり自信なし。steamrollerには「圧制者」の意味もあるらしいので道路の比喩と掛けてるんだと思う)
In theory, there is no difference between theory and practice. But, in practice, there is.
理論上は理論と実際に違いはない。しかし、実際には違いがあるのだ。
Jan L. A. van de Snepscheut
The hardest part of design … is keeping features out.
デザインでもっとも難しいのは……特色を出しつづけることだな。
Before software can be reusable it first has to be usable.
ソフトウェアを再利用可能にする前に、まずは利用可能にしよう。
Ralph Johnson(デザインパターンのGoFの一人らしい)
If debugging is the process of removing bugs, then programming must be the process of putting them in.
デバッグがバグを取り除く行為であるなら、プログラミングはバグを注入する行為のはずだ。
Software and cathedrals are much the same - first we build them, then we pray.
Anonymous Preacher
ソフトウェアと大聖堂はとてもよく似ているーー我々はまずそれらを作り、そして祈る。
無名の伝道師
The goal of Computer Science is to build something that will last at least until we’ve finished building it.
Anonymous Consultant
コンピュータ科学のゴールとは「何か」を構築することだ。少なくとも我々がそれを構築し終えるまで使い続けられる「何か」を。
無名のコンサルタント
The software isn’t finished until the last user is dead.
Anonymous Support Group Member
最後のユーザーが死ぬまでソフトウェアに終わりはない。
無名のサポートグループメンバー
Better train people and risk they leave - than do nothing and risk they stay.
Anonymous Technical Trainer
彼らを鍛えてリスクから遠ざけるのはいいことだ。何もせず彼らをリスクにさらしつづけるよりは。
無名のテクニカルトレーナー
Programming is 10% science, 20% ingenuity, and 70% getting the ingenuity to work with the science.
Anonymous Scientist
プログラミングは科学が10%、創意が20%、そして創意を得るために科学するのが70%だ。
無名の科学者
All programmers are playwrights and all computers are lousy actors.
Anonymous Hack Actor
すべてのプログラマは脚本家であり、すべてのコンピュータはどうしようもない役者だ。
無名の大根役者
Bad code isn’t bad, its just misunderstood.
Anonymous Code Behaviorist
悪いコードは悪くない。ただ誤解されてるだけだ。
無名のコード行動主義者
It is easier to measure something than to understand what you have measured.
Anonymous Analyst
君が値踏みされたのは、「分かる」より「測る」ほうが簡単だからさ。
無名のアナリスト
The sooner you get behind in your work, the more time you have to catch up.
Anonymous Scheduler
仕事の遅れを取り戻そうとする時間よりも、仕事が遅れる時間のほうが先に来る。
無名のスケジュール管理者
When a programming language is created that allows programmers to program in simple English, it will be discovered that programmers cannot speak English.
Anonymous Linguist
プログラミング言語がつくられたときは簡単な英語でプログラムできるようにと考えられているが、そのうちにプログラマは英語が喋れないことを発見するだろう。
無名の言語学者
Benchmarks don’t lie, but liars do benchmarks.
Anonymous Tester
ベンチマークは嘘をつかないが、嘘つきはベンチマークする。
無名のテスター
]]>Why do we never have time to do it right, but always have time to do it over?
Anonymous Code Monkey
僕らがそれをする適正な時間なんてものはないのに、どうしていつも僕らは時間を費やしすぎてしまうんだ?
無名のコードモンキー
Joel Spolskyの意見とコメント欄でのやりとりがどっちも面白かった。
僕のテストの書き方としては、テストファーストではなく、思いつきで処理を実装していくうちに仕様のようなものが見えてきたらテストを書いて保証する、というスタイルが多い。コードカバレッジには今のとこあまり興味がない。
「仕様のようなもの」というのは、「このメソッドはこういう値を返すべきだ」というような挙動の定義を指す。RSpecが「obj.foobar.should == "barbaz"」のように「should(〜すべき)」といった形式でテストを書かせ、テストをまとめたファイルを「spec(仕様)」と呼ぶのはたぶん偶然ではない。
これらは厳密な意味でのTDDとは違う(ただのユニットテスト論?)かもしれないけれど、とりあえずはこういうスタイルでやってて上手くいってる。
概念や私見はこれくらいにして、実際にどういう使い方をしてるのか書いてみようと思う。
PHPでデータベースにアクセスするちょっとしたDAOクラスを書く機会があった。僕に管理権限のないサーバなのでDoctrineやPEAR::MDB2といった既成ライブラリを使うには丸ごとアップロードするしかなく、それはちょっと大仰だったのでスクラッチで書くことにした。
それでまあいろいろあって、OFFSETやLIMITをどう実現するかといったところに行き着く。もちろんOFFSETやLIMITの値を引数で指定するわけですが、"OFFSET -7.77"とか"LIMIT a"みたいになったら困るので、引数が整数かどうかをちゃんとチェックする必要がある。
PHPは型の扱いがでたらめ(参考:2a問題)な上、それが整数かどうかを判定する便利関数もないので(is_intはstring型だとfalseを返す、is_numericは小数や8進や16進でもtrueを返す、ctype_digitは負数だとfalse)、自分でis_intメソッドを書くことにした。
<?php // 実際には他のメソッドもありますが省略してます。 class Model{ function is_int($var){ // 整数ならtrue。intやstringじゃなければfalse return is_int($var) || (is_string($var) && preg_match("#^-?[0-9]+$#",$var)) ; } }
なんか複雑な感じになってしまった。最後のpreg_matchだけでいいような気もするけど、$varが何かのオブジェクトで__toStringの実装次第ではtrueを返すかもしれないとか考え出すとなんとなくこうなった。
さて。
実装しては見たものの、これがちゃんと動くかどうかあまり自信がない。試してみる必要がある。どうやって試す?
軽く試すだけなら実行用にtest.phpなんかを書いてそこで動作確認するだけでもいい。もちろんphp -r ""でコマンドラインから試してもいい。でもテストすべき境界が多いし、0や"0"はどう? -1はちゃんとtrueになる? このメソッドちゃんと動いてる? などなど疑心暗鬼になるたびにいちいち書いて試すのは効率的じゃない。
PHPUnitを使うことで、このメソッドが仕様どおりに動作してると保証してやることにする。公式サイトのとおりにインストールしたら準備完了。いよいよテストを書く。
ちなみに各ファイルの構成はこういう感じ。
Model.class.php
tests/
Model.php
<?php require_once 'PHPUnit/Framework.php'; require_once dirname(__FILE__)."/../Model.class.php"; class ModelTest extends PHPUnit_Framework_TestCase { function setUp(){ $this->model=new Model(); } function testIsInt(){ // こいつらはtrueを返すべき $this->assertTrue($this->model->is_int(1)); $this->assertTrue($this->model->is_int(0)); $this->assertTrue($this->model->is_int("0")); $this->assertTrue($this->model->is_int(-99)); $this->assertTrue($this->model->is_int("23")); $this->assertTrue($this->model->is_int("-1")); // ここからはfalseを返すべき $this->assertFalse($this->model->is_int(null)); $this->assertFalse($this->model->is_int("string")); $this->assertFalse($this->model->is_int("23.2")); $this->assertFalse($this->model->is_int(array())); $this->assertFalse($this->model->is_int(array(0=>0))); } }
コマンドラインから実行してみるとだいたいこういう感じの結果が出てくるはず。
# phpunit tests/Model.php PHPUnit 3.3.4 by Sebastian Bergmann. . Time: 0 seconds OK (1 test, 11 assertions)
テストメソッドを1個(testIsInt)、一致するかどうかのチェックを11個やったよ、全部期待通りだったよ、とのこと。これ以後、何か気になる条件を思いついたらtestIsIntの中に適宜追加していけばいい。たとえば、"-1.0"を渡すとどうなる? あるいはどうなって欲しい?
Model#is_intを作った当初の目的は、OFFSETやLIMITのためだった。"OFFSET -1"や"LIMIT 0"は要らないと気づく。つまり、整数かどうかのチェックではなく、正の整数かどうかのチェックが欲しくなる。
Model#is_intを変更してもいいけど、ここではModel#is_positive_intを書くことにする。どうせならModel#is_intを使って手っ取り早くやってしまおう。
<?php class Model{ function is_int($var){ // 整数ならtrue。intやstringじゃなければfalse return is_int($var) || (is_string($var) && preg_match("#^-?[0-9]+$#",$var)) ; } function is_positive_int($var){ // 正の整数ならtrue。0はfalse return $this->is_int($var) && $var > 0; } }
テストするまでもないように思えるけど、これもちゃんとPHPUnitでチェックするようにしておこう。疑心暗鬼はこういうところから忍び寄ってくる。
<?php require_once 'PHPUnit/Framework.php'; require_once dirname(__FILE__)."/../Model.class.php"; class ModelTest extends PHPUnit_Framework_TestCase { function setUp(){ $this->model=new Model(); } function testIsInt(){ // こいつらはtrueを返すべき $this->assertTrue($this->model->is_int(1)); $this->assertTrue($this->model->is_int(0)); $this->assertTrue($this->model->is_int("0")); $this->assertTrue($this->model->is_int(-99)); $this->assertTrue($this->model->is_int("23")); $this->assertTrue($this->model->is_int("-1")); // ここからはfalseを返すべき $this->assertFalse($this->model->is_int(null)); $this->assertFalse($this->model->is_int("string")); $this->assertFalse($this->model->is_int("23.2")); $this->assertFalse($this->model->is_int(array())); $this->assertFalse($this->model->is_int(array(0=>0))); } function testIsPositiveint(){ $this->assertTrue($this->model->is_positive_int(1)); $this->assertTrue($this->model->is_positive_int("23")); $this->assertFalse($this->model->is_positive_int(0)); $this->assertFalse($this->model->is_positive_int(null)); $this->assertFalse($this->model->is_positive_int("0")); $this->assertFalse($this->model->is_positive_int(-99)); $this->assertFalse($this->model->is_positive_int("-1")); } }
テストを実行してみる。
# phpunit tests/Model.php PHPUnit 3.3.4 by Sebastian Bergmann. .. Time: 0 seconds OK (2 tests, 18 assertions)
ちゃんと期待通りの挙動になってるようだ。
ここでは「整数かどうかのチェック」というあまり現実的じゃない例になってしまいましたが、実際には他に「Model#to_sqlが期待通りかどうか」「Model#escapeはちゃんと文字列をSQLエスケープしてくれてるか」「WHERE句の指定はちゃんと動いてるか」などもテストしました。
テストは開発中の疑心暗鬼を取り除いてくれた。疑心暗鬼にかられたときに何をすべきかを明確にしてくれた。バグ修正やチューニングの際にis_intメソッドを変更することがあっても、テストを通すことで安心できる。Model#is_intがきちんと動いてるから、Model#is_positive_intは安心してModel#is_intを利用することができた。また、OFFSETやLIMITのあたりで変な挙動をしたら、それは少なくともModel#is_intのせいではないはずだし、Model#is_intのせいであるならテストが漏れてたってことで、変な挙動をする引数をテストに追加して淡々と修正すればいい。
これらは"OFFSET -7.77"や"LIMIT a"が発生しないことを保証するために必要なことだ。YAGNIの思想とはまったく矛盾しないと思う。
ていうか、Joelが挙げてる「上手くいくケース」をより詳細に書いてみただけになりましたね。あちゃー
]]>http://github.com/tt25/haremie/tree/master
手順はgithubのREADMEにも書いてますが、
でとりあえず一通り動かせるはず。
YAML書き直す→ruby run.rb→ramazeって手順がイケてないなあと思いますが、そのへんはそのうちなんか考えます。
将来的にはImagefapとか4UとかGIGAZINEとかにも対応したい。あとなんか変な英語の看板を上げてるサイトとかlolcatまとめみたいなのとかもあったはずだけどURLがわからないので見つけたら対応したい。
自分でもなんか書きたい!という奇特な方がいらっしゃいましたらlib/scraper/*あたりを見ていただければだいたいAPIがわかるかと思います。
]]>githubのブログで見つけた。面白そうだったので訳してみる。
原文では「DVCS」と書いてますが、ここでは「分散SCM」で統一してます。同様にVCSはSCMです。ググったらそっちのほうが数多かったので。分散SCMが和製英語とかではなくて、AtomPPとAtomPubみたいなものです。たぶん。
あとfamiliarの訳が「身近」だったり「馴染みのある」だったりといまいち安定しませんが、スペースアルクによると「造詣が深い」「親しい仲間」とかいう意味もあるらしいです。たぶんfamilyと似たようなニュアンスかと思います。
ちなみにbzrはBazaar、hgはMercurial、svnはSubversionです。それぞれの略称はコマンドに由来します。
GNOMEの分散SCM調査が完了した。1週間半ほど前に、579人の異なったSVNアカウントを持つ人々から集計した(コミット権を持つ人は全部で1083人居る、つまり約53%の回答率だ)。この調査は、2009年にGNOMEプロジェクトがSVNから他の分散SCMへと移行することが可能かを把握するために実施された。従って、ここでの質問は分散SCMではないSVNを侮辱的に見るようなものになっている。結果は下に載せた。なお、データはここで、グラフを描くために使ったスクリプトはここでそれぞれ見つかるだろう。
簡単なプレゼンを2度おこなっている。ひとつめは個々の質問に対する回答者の割合を、次に回答者が他のSCMに関する質問にどう答えたかをそれぞれ載せている。私が考えられるかぎり、もっとも中立なグラフだ。
目を引いたものには私のコメントや分析も併記している(集計前にいろいろと予測していた。ほとんどはその通りとなったが、意外な結果もあった)。先入観のないコメントはつけようがない。実際には、私は調査結果が指し示す明白な筆頭候補に注意を引かれ、それがもっとも有用なものだと考えるに至った。つまり、私のコメントは多数派にフォーカスしている。そういったバイアスが不要なら、私のコメントを無視し、データから自身の結論を見出してほしい。
まずは質問内容を確認しておこう。
(注:私は択一式の回答を期待していた。結果を見ると実に面白いことに、「すべて」という答えがあった)

ドキュメンターの少なさには思わず引きつけられる。ドキュメンテーションは「その他」カテゴリーだと思ったんだろうか(多くのドキュメンターが「自分はここだろう」と判断したんだろうか?) ある種のボランティアはこのカテゴリに引きつけられた? 我々は何かどこかで根本的なミスを犯したのか?

おお。すさまじい数の人たちが他のSCMよりもgitと答えている。60%以上の人にとってgitは身近で、半数近くが頻用してるって? あそこには多くの人がいることを知っているが、これほどとは思わなかった。bzrとhgもコミュニティ内で適度な強さを示している(31人が3つすべてに親しんでいて、そのうち1人はすべてをよく使うと言っている。いや、私じゃないよ)。
多数の人がgitを頻繁に使っていて、他の2つを引き離している。それら2つ(あるいは、少なくともbzr)は今さらに知られているだろうが、私が提案することはもうないだろう。
「Average rank」は他の5つから算出した。もしコミュニティが二分されているなら、あるいはどのSCMが使われるのか気にしないのであれば、すべてのSCMはrank 3になるはずだ。「Average rank」においてそれぞれのSCMがrank 3からどれだけ離れているかが意味を持っている。
(原文と同じ画像)
値は正常なものだが、それぞれのグラフで縦軸の幅が異なってしまっている。申し訳ない。
このグラフには非常に驚いた。gitは最高(rank 1)と最低(rank 5)がもっとも多くなり、評価が分かれるだろうと考えていた。たしかにrank 1は断トツだったが、rank 2で収束し、そこからは他のSCMと目をみはる差をつけた。この結果に私は打ちのめされた。
どのSCMがどういった人に支持されているのかに興味を持った。例えば、「その他」と回答した無視できない数の人々が、別のバージョン管理システムへと変更すべき回答した。彼らは何を支持したのだろう? 翻訳者やテスターは、開発者に比べて違うシステムを気に入ってるんだろうか? 複数のモジュールを保守するメンテナは、メンテナではない人と違う視点を持っているのではないか? すなわち、このセクションではこれらの問いに挑戦してみる。なお、丸括弧()の中にはそのグラフに関係する人数が書かれている。

SCMの好みにメンテナか開発者かは関係ないようだ。しかしながら、bzrは単一モジュールのメンテナ/開発者にもっとも強く支持され、gitは複数モジュールのメンテナ/開発者に支持されている(Mercurialは非メンテナ/非開発者に一番強く支持されている。考えるに、これは後者の人々は強い意見を持たないことの反映だろう)。これはbzrとgitの設計方針に対する私の直感と一致する。どちらもそういった用途に最適化されており、それがこの結果にも反映されているのだろう。しかし、それでもこの差は無視できるほどの規模でしかないというのは正しいだろう。

どれもそれほど分散していない。期待していたのは、頻繁なコミッターは非コミッターよりも強い意見を持っている(rank 3から遠くなる)ことだった。

このグラフは驚きだ。gitは専ら開発者にのみ支持されると思っていたが、一見したところではドキュメンター以外のすべてのグループから支持されている。ドキュメンターはgitに最低ランクをつけている。
微小な母集団(ドキュメンテーションを「メイン」にしているたった4人)による最後のグラフを破棄するよう勧める。この主張によるメリットは、もっとも興味深いグラフ(すなわち何人かのSCMジャンキー)は非SCM
翻訳者のグラフにも興味深い点を見つけた。この集団においてはgitが他のSCMより圧倒的優位というわけではない。正直なところ、翻訳者集団においてはgitとSVNが同列で、もちろんそれはわずかにリードしている。

特に驚きはない。気に入っているとおりだ。使えるかよく使うかにかかわらず、馴染みのあるSCMを支持する傾向がある。しかしながら、gitはすべての場合においてポジティブな評価に恵まれているか、少なくとも2位につけているのでは?

彼らは、我々がgitへ移行すべきだと考えている。他に強く支持するものはないし、gitを使うことに賛成だ。「気にしない」「なぜ移行するのか?」「移行すべきじゃない」のグループはみんなSubversionを支持している。後者のグループ内でも、「気にしない」「どうして?」のグループではgitは2番手につけている。
コミュニティ内部では移行を強く支持しているように見える。また、コミュニティ内ではgitがsvn,bzr,mercurialよりも強力に支持されている。
SCMに関わらない人たち(テスターとドキュメンター)はgitは2番手であり、支配的といえるほどのリードではない。テスターのあいだではそれでもgitは支持されており、SVNより上位だ(bzrとhgはそれらに及ばない)(訳注:テスターと翻訳者を勘違いしてるよね?)。ドキュメンターのあいだではgitは大差をつけて最下位だ(bzrが圧倒的な首位だ)。これがどうしてだかはわからない。
SCM利用者の集団では、彼ら自身のお気に入りのシステムを推している。gitは常に2位につけている。
(以下、しばらく上に書いたことの繰り返しなので中略)
私の分析が偏っていたら申し訳ない。最初に言ったように、気軽に私の分析を無視して自分自身でこのデータから自身の結論を見出してほしい。
]]># gem search ramaze *** LOCAL GEMS *** ramaze (2008.11)
# ramaze -v Ramaze Version 2008.10, on ruby 1.8.6 (2007-09-24) [i486-linux]
なんでバージョンが違うんだ。まあ何にしろ現時点での最新版です。
の一人二役をやっているので、require "start"した途端にサーバが起動して困ります。
このうち1.のrequire部分をinit.rbなどにカットアンドペーストして、跡地にrequire "init"を置きましょう。
# init.rb require 'rubygems' require 'ramaze' # Initialize controllers and models require 'controller/init' require 'model/init'
# start.rb require "init" Ramaze.start :adapter => :webrick, :port => 7000
ramaze/spec/helperをrequireしてしまうと、(たぶん)Bacon定義とRSpec定義がオーバーラップするなどして上手くいかなくなります。かといって単に外してしまうとget(path)などのメソッドが使えません。
# spec/helper.rb require File.join(File.dirname(__FILE__),'/../init') require "ramaze/spec/helper/mock_http" # clean STDOUT Ramaze::Log.loggers=[] Ramaze.start({ :adapter => false, :run_loose => true, }) describe "http",:shared => true do before do extend MockHTTP end end
これでMockHTTPが持つget,put,post,deleteなどがitの中から使えるようになります。
# spec/main_controller_spec.rb require "#{File.dirname(__FILE__)}/helper" describe "specサンプル" do it_should_behave_like "http" # <= 必須 it "トップページを正常に取得" do got=get("/") got.status.should == 200 end it 'リダイレクトを追う' do got=get("/redirect_to_toppage") # Ramaze側に"/redirect_to_toppage"がマップされてないならここでエラー。 got.status.should == 302 got.header[:location].should == "/" end end
コンソールから"spec spec/main_controller_spec.rb"でいけます。
]]>↑この「puts items.sql」が、
SELECT * FROM "items" WHERE ("id" IN ( SELECT "items"."id" FROM "items" INNER JOIN "items_categories" ON ( ("items_categories"."item_id" = "items"."id") AND ("items_categories"."category_id" IN (SELECT "id" FROM "categories" WHERE ("user_id" = 3))) ) )) ORDER BY "pubdate" DESC LIMIT 100
↑こうなる(実際にはSQLに改行入りません)。
PgAdminIIIで普通にSQL書いたあと逐語的にRubyへ書き換えたのでどっか変かもしれないけど、一発で動いたのは感動した。
Sequel: The Database Toolkit for Ruby
Categoryって名前のせいで無駄なことをしてるように見えますが、やりたいことを説明するにはTagのほうがより適切な名前です。
はてなブックマークを使って説明すると、User=自分、Category(以下Tag)=はてなブックマークのタグ、Item=そのタグが持ってる記事、って感じになります。
tag:{
"これはすごい":
- たった100個の冴えたやりかた
- GIGAZINE: いまから安価でPR記事書く
"これはひどい":
- GIGAZINE: いまから安価でPR記事書く
- 年越しそばおいしい
"lifehack":
- たった100個の冴えたやりかた
- なぜさおだけ屋はB29を撃墜できるのか
}
というようなブックマーク状況のとき、自分(USER_ID=3)は「これはすごい」「これはひどい」のタグを監視してるとします。
ここで自分が監視してるタグがついた記事をアグリゲートすると、「たった100個」「GIGAZINE」「GIGAZINE」「年越しそば」となり、GIGAZINEが重複してしまいます。なのでいったんサブクエリ化してitem_idだけを抽出し、一番外側のクエリでINすることでItemの重複を回避しようとしています。
]]>ちなみに会社ではメインがOperaで開発用がFirefox。家はLinuxなのでOperaとFirefoxとwine上のOpera(ニコニコ動画用)で計3つ。
たとえばFlashをいっぱい埋め込んだページを開いたらリソースを大量に食う。これによってOperaのキャッシュがスワップアウトしたりexplorerがもっさりしてOSが全体的にすっとろくなる。まあ、これはブラウザがというよりFlashのせいだからどうしようもない。問題は別にある。
OperaならF12キー、FirefoxならPrefBarやNoScriptあたりでFlashのオンオフを制御できるけど、Chromeの場合はそれがない。というかFlashオンオフのオプションすらない。仕方ないのでウィンドウのどっかを右クリック→タスクマネージャ→Flashプロセスをkill、として間引きしてる。
せっかくのGreasemetalなんだからUser Script使って対処できなくもないけど、会社用サブブラウザのためにUser Script書くのも探すのもめんどいなあといった印象。
嫌儲とかではなくて、単にニュース記事の右端や上端でちらちらされると鬱陶しいから消したい。せめて動きを止めたい。Operaならコンテンツブロックか「GIFアニメーションを無効にする」、FirefoxならAdBlockあたりで落ち着いた環境になるんだけど、Chromeはそのへんが出来ない。前述の手順でFlashプロセスを殺しても代替としてアニメーションGIFが出てくるのであまり意味がない。もちろんGIFアニメを止める機能なんてない。
ウィンドウ幅を縮めて広告を領域外へ追いやるか、堪えるか、あまりに鬱陶しければCtrl+L Ctrl+C Alt+Tab Ctrl+T Ctrl+BしてOperaでページを開きなおしてる。
なんか全然違うところにスクロールする。レンダリングを終える前にジャンプしてしまってて、画像とかCSSによってアンカーの場所が変わっても追随しない感じ。
レンダリング完了後にCtrl+L Enterでだいたい正しい場所へ飛んでくれるけど、なんというか、挙動があわてんぼすぎる。落ち着け。
タブの使い勝手がIEなみ。これは使い始めたころから思ってたんだけどまったく慣れない。どんだけ軽いだの早いだの言ったって、あんなUIじゃタブ20個も開けない。
いっぱいタブがあるとひとつひとつのタブ幅が狭いので、タブを切り替えるつもりが閉じてしまうなんてことがしばしばある。Ctrl+Shift+Tを知ってから復旧は助かってるけど、せめてタブを閉じるボタンくらい非表示にさせて欲しい。
だいたいのタブブラウザは「開いてるタブの左or右or上or下に」「常に最後尾へ」のどっちかのスタイルを設定で選択するけど、Chromeの場合はどっちの挙動でもない。強いていえばFirefoxのツリー型タブと似てる。
たとえば|A|B|C|とタブを開いていて、Bにあるリンクを新しいタブで開いたとすると、フォーカスはBのタブにあたったままで、新しいタブがBとCのあいだに出てくる。この新しいタブをB'とすると全体は|A|B|B'|C|になる。ここでBからもう一度新しいタブを開くとB'とCのあいだに入る、つまり|A|B|B'|B''|C|となる。この状態で、B'から新規タブを開くとB'とB''のあいだに出現する。
長時間タブを開きっぱなしにしてると、どのタブをどのタブから開いたかなんて覚えてないので、手札が|1|2|3|4|5|6|7|のとき、3のタブから新規タブを開くとどこにタブが出てくるかまったく見当もつかない(いちおう「3|?|4」〜「7|?」のどっかだということはわかるけど範囲広すぎ)。
そしてさらに具合の悪いことに、Chromeは既読タブと未読タブの区別ができない。
これは自分でも意外だったけど、無意識のうちにウィンドウのタイトルバーを見てページタイトルを確認してたみたい。
Chromeでページタイトルを確認するには、タブにポインタを置いて0.7秒ほど待ったら出てくるツールチップを確認するしか手段がない。これは予想外にストレスフルだ。
ただでさえタブ10個くらい開いただけで見えるタイトルが最初の1文字か2文字になるのに。そんなにタイトル見せたくないんか。
なんか知らないけどURLが長いときは中抜きされたURLが左下に出てくる。ドメイン名とURL末尾らへんはちらっと見えるけど、なんでこんな省略処理をしてるのかわからない。そんなにURL見せたくないんか。
そりゃそうだ。Chrome以外のアプリも重いんだからChromeだけ軽快なわけがない。
タブのしょぼいUIが雄弁に物語ってるように、あるいはデフォルトでFirefoxでいうPrism機能やOperaでいうスピードダイアルを搭載していることからわかるように、特定少数のウェブページを使うためのブラウザ。ウェブページは量より質、そんな思想が感じられる。
使用感はSafariやIEと似てたものの、それぞれSafariは便利さよりエレガントさ、IEはユーザーよりカスタマーって感じだからどっちもChromeとは方向性が違うとは思う。
少なくとも僕とは相容れないことがわかった。会社でiGoogle見るぶんにはぼちぼちだけど、メインには永久にならなさそうだ。
]]>
http://my.opera.com/chooseopera/blog/2008/10/01/typical-browser-usersが面白かったので訳してみた。
他はたぶん意訳。
]]>あとsudo a2dismod php5してPHPにさよならした。これによってtopでいうと、
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 25785 nobody 18 0 146m 6256 1632 S 0.0 0.2 0:00.00 apache2
が
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 25856 nobody 15 0 83592 3932 1700 S 0.0 0.1 0:00.00 apache2
になったけど、そのぶんmerbプロセス(デーモン)とpostgresプロセスが増えたのでプラマイでいうとマイナス。
]]>ChromeがWineで動くならニコニコ動画をPrism化して専用ブラウザとして使おうと思ってたけど、インストールすら出来なかったので断念。
Firefox3は「ツリー型タブ」とか「Vimperator」とか「Ctrl+Tab」を使えば100近くのタブもそこそこ取り扱えるので、ヘビーユース=Operaって図式は年々あやしくなってきてるかなあ。
Opera(全バージョン):へー
Firefox(3.0、3.1(TraceMonkey)):すげー! これでシェアまた増えるんじゃね?
Chrome:すげー! これだいぶシェア取れるんじゃね?
Opera:なんか慣れない。気持ち悪い。
Chrome:当然といえば当然。
Opera:へー
Firefox:WindowsでもMacでもLinuxでも使えるなんて素晴らしい!
Chrome:WindowsでもMacでもLinuxでも使えるなんて素晴らしい! 早くMac/Linux版出ないかなあ。
Opera(Opera Link):へー
Firefox:Google Browser Sync便利! 開発終わったけど似たような機能のアドオンがあるからいいよね!
Chrome:まだベータ版なのでノーコメント。
Opera:iniファイル触るのって敷居高いなあ。
Firefox:about:configを開いてxxxxというキーを作りxxxxxという値を設定して、次に……→すげー!
Chrome:ノーコメント。
Opera:でも拡張性がなあ……
Firefox:カスタマイズしてこそだ!
Chrome:マウスジェスチャもコンテンツブロックもユーザースクリプトもないし、使えるようにもできないけどとにかくすげえ!(マウ筋使えばいちおうマウスジェスチャは出来る)
Opera:ユーザー側で機能追加も出来ないしダメだなあ
Firefox:それ(アドオン|Greasemonkey|userChrome.js)で出来るよ。あるいはつくるよ。
Chrome:機能の絞りかたがすばらしい! あるいは将来に期待!
Opera:そーなんだ
Firefox:無限alert作り込んだやつが悪い
Chrome:すげー便利だ!
別にすねてないです。この温度差はやっぱブランドとかそのへんのあれなのかなあ。
]]>Ubiquityについてはhttp://www.itmedia.co.jp/enterprise/articles/0808/31/news003.htmlとかhttp://journal.mycom.co.jp/news/2008/08/28/035/index.htmlとかhttp://d.hatena.ne.jp/teramako/20080827/p1を参考のこと。
でだいたいどんなもんか体験できると思います。
]]>らしいので書いた。/entry/json/まで空になってた。
http://b.hatena.ne.jp/entry/http://d.hatena.ne.jp/fk_2000/20080829/p1
ドメインが同じなのでhttp://b.hatena.ne.jp/bookmarklist?url=http%3A%2F%2Fd.hatena.ne.jp%2Ffk_2000%2F20080829%2Fp1&mode=rssをAjaxってDOMってる。
会社にあったのでたまに読んでたけど、理論先行というかサンプルコードがなかったのでいまいちピンと来なかったなあ。というか、本を読んでプログラミングの何かが上達したと思えたことがない。手を動かしていろいろしないと頭に入らないタイプなんだと思う。
一方JavaScriptのPrototypeは触っててなるほどなあと思った。Array#mapとかそうあるべきだなとしみじみ思った。あと[1,2,3,4,5].find_all(..).map(..).each(..)みたいにつなげて書ける(メソッドチェイン)のはすごいと思った。これはurlencode(mb_string(trim($str),"SJIS","UTF-8"))みたいな書き方になれたPHP脳を活性化するのに充分な刺激だった。
ActiveRecordのrdocというか、解説を読んでさらに理解が深まった。DBのテーブルがクラスに、その中の1行が1インスタンスに対応してて、save,deleteその他いろんな操作がメソッドとして提供されてる。アソシエーションも直感的に扱える。これは毎回DBのラッパー(O/Rマッパーとはいえない)を漸進的に改良しつつ使ってたPHP脳を活性化するのに充分な刺激だった。
ActiveRecordもPrototypeもつくるのは大変だろうけどすさまじく使いやすい。と思ったけど作るのも思ってたほど難しくはない。
jQuery以前のころにPrototypeを真似て、自分でArray.prototypeを拡張してArray#maxとかArray#sumとかを書いてみたけど簡単なものなら1〜3行くらいで完了する。prototype拡張じゃなくどっかにfunction array_sum()みたいなものを書くより手っ取り早いしわかりやすいし保守も使用も楽だ。さすがにActiveRecordを自作は出来ないけど、それはオブジェクト指向だからではなくもっと別の要因が大きい。
あとあんまり関係ない気もするけど、Head Firstのデザインパターン本はけっこうおもしろかった。いま見たらオブジェクト指向の姉妹本?もあったので読んでないけどたぶんおもしろいはず。
Head Firstデザインパターン―頭とからだで覚えるデザインパターンの基本
Head Firstオブジェクト指向分析設計 ―頭とからだで覚えるオブジェクト指向の基本
もちろん理論から入ったほうが頭になじみやすい人も居ると思うけど、getter/setterだとかinterfaceがどうだとかよりおいしそうなとこからつまみ食いすればいいとおもう。
]]>普段でたらめな環境で仕事してるので、しっかりした人たちが集まってわいわいやってるのはとても勉強というか刺激になった。物理的に2日目いきたかったなあ。すごく残念だ。次なんかあったらぜひ行こう。
以下は聞きながら取った雑感メモをちょっとは日本語らしくしたものです。文中のセリフなどはオリジナルを無視して書きやすい形に再構成しています。その過程で何かが抜け落ちたり誤解してたりするかもしれません。ご注意ください。
リクルートの人がノリよかった。Sunのスーツの人が絵に描いたように完璧な人だったので感動した。これが大企業の実力かと思った。Sunすげえ。
Wiiリモコンのセンサーを使ってFlashを操作する(Win用のドライバ?はすでにある)。Wiiのセンサでなくとも赤外線さえ出してればいいので、ライターの火を使って画面を操作するなどが可能→Googleマップと合わせて懐中電灯みたいにする
マッシュアップはアイデア勝負。一人で開発すると会議しなくていい(誰かがネガティブなこといって推進力を奪うなどがない)。
NetBeans(IDE)を使ってJRuby on Railsのさわりをちょっと。JRubyはJavaのライブラリを呼び出せる。Railsのscaffoldにちょこちょこ(6行くらい)足して、リンクをクリックすると(Javaのライブラリを使って)文中の英単語を読み上げさせる→これだとサーバ側で再生されるのでデータセンターの怖い話でしかないけど、まあこういうことも出来ますよというデモ。
マッシュアップアワードの1等賞金は100万円。
ステッカーとかリクルートのペンとかなんかノベルティいっぱいもらった。
時間がなかったので質問できなかったのが残念。
DRBDは大雑把にいうとネットワークを使ったソフトウェアRAID1(ミラーリング)。HDDを全然信用してない僕は当然RAIDコントローラもあまり信じてない。分散してくれないと安心できない。DRBDのDは分散(distribute)のD。
OSのディスクとのやりとりをフックして、自前(ローカル/プライマリ)のHDDへデータを書き込むついでにネットワークごしに別マシンのHDD(セカンダリ)へも書き込み指示を出す。/dev/sda1とかじゃなくて、/dev/drbdみたいなものをマウントして使う。
信頼性レベルは3段階あり、それぞれ高い順に
ミラーリング完了とする。普通は1か2。1でもそれほど(直感的に思うほど?)パフォーマンスは落ちないらしい。あとメタデータと照合して、セカンダリが一時的に応答しなくてもあとで勝手に同期するらしい。
いわゆるクラスタリング。DRBDはただのミラーリングなので、死活監視とかフェイルオーバとかをするなら何かと連携する必要がある。
Heartbeatを使ってプライマリが死んだら自動的にセカンダリがプライマリになるようにいろいろ。
プライマリが死ぬ ↓ セカンダリがDRBDをマウントして新しくプライマリになる ↓ プライマリ復活 ↓ 旧プライマリがセカンダリになる
HDDを全然信用してない僕は共有ディスクなんてものがこの世にあることを受け入れられない。まあディスクがHDDとは限らないし、わからんでもないんだけど、それでもなあ。
ということであんまり聞いてなかった。DRBDのほうが優れてるのは言われなくてもわかってる。
/deb/drbdみたいなものをマウントできるのはプライマリだけ。セカンダリはマウントできないし無茶するとデータ壊れる。そこでLVMスナップショットを使う。テラバイト級のデータでもLVMはほぼ一瞬でイメージがつくれる(マウント出来るの聞き違えだったかもしれない。自信なし)。
LVMイメージをバックアップすることでデータのバックアップを実現する。
実例。メールサーバは1メール1ファイルなMailDirですけど、増えてくるとファイル数が数千〜億とかいう単位になってくる。rsyncがこれらのファイルの更新チェックだけで丸一日掛かったりしてまともに機能しない。DRBDならデータを書き込んだときには既にミラーリングが完了しているので安心。ここでペッシとプロシュートに意識が飛んだので詳細はちょっと聞き逃した。
疑問1:スピーカーが使ってたタスク切り替えアプリが気になった(OSはおそらくLinux)。空中に各ウィンドウが浮かんでて、マウスで泳いでたどり着く感じのものだった。Compiz関係かなあ。
疑問2:DRBDで3台以上のHDD(ていうかマシン)を管理できるのか否か。出来るのであればミラーリングに限らずRAID5とかRAID1+0みたいなことも可能か。
疑問3:MogileFSと比べてどうなのか。
疑問4:話を聞いたかぎりだとあんまりスケールしなさそうだけどそのへんはどうなのか。
仕事でMySQL 3.23と4.0と4.1を同時に触らされて日本語とかSQLクエリとかで散々な目に遭って以来、MySQLには警戒心しかないんですがPostgreSQLにも役立つ部分はあるかと思って見てみた。Postgresとはあんまり関係なかったけどInnoDBの内部構造がちょっとわかっておもしろかった。
4GBのCSVファイル(だいたい800万行くらい)をLOAD DATAでデータベースにロードするのに、何も考えずにそのままやると13時間くらい掛かる。予め主キーでソートしたデータであれば14分くらい。桁違い。13時間掛かったほうは14分のほうに比べて、SELECTで参照するのも圧倒的に遅い。ロードするのもした後も何もかも遅い。
この現象はなぜ起きるのか、解決策は、などを検証データを用いて説明。
この現象はInnoDBのデータ構造によって起きる。順番ばらばらのデータを入れると、B-Tree構造が行き当たりばったりというかしっちゃかめっちゃかというか、とにかく乱雑で非効率なものになってしまう→OPTIMIZE TABLE構文はこれを整理する。
解決するだけならOPTIMIZE TABLEでなんとかなるけど、これ自体の実行時間も桁違い。OPTIMIZE TABLEした後はどっちも同等の速度が出るようになる。でも、どうせならソート済みデータを入れたほうがいいよね。sortコマンドも数分程度で終わるわけだし。
大きなデータはソートしてから投入しましょう。
Q. 検証結果の表をみると1GBの場合がやたら早くなってるように見えますが、何か理由やノウハウなどがあるのでしょうか?
A.
たぶんメモリにバッファが残ってるからディスク読みにいかなくてよかったことが大きいんだと思う。ノウハウというか、InnoDBなら64bit OSをつかってメモリバッファをできるだけ大きくしましょう。
Q. MyISAMでも同様の現象/効果が認められますか?
A.
InnoDBしか検証してないのではっきりしたことはいえない。でもインデックスが絡むのでだいたい似たような結果にはなると思う。
Q. パラメータチューニングでどうにかなりますか?
A.
多少の効果はあるでしょうが、13時間が数十分になるとか見たらどうでもいいレベルではないかと思います。やるとしたら、先ほどもいいましたがメモリキャッシュを大きく取るのが有効かと思います。
Q. パラメータをいくつか設定して普通にバルクインサート(INSERT INTO foo (id,name) VALUES (1,'foo'),(2,'bar'),(3,'baz')みたいなやつ)するだけでもなかなかいい感じの速度が出ていますが、LOAD DATAと比べてメリット/デメリットなどは?
A.
それは興味深いですが検証出来てないのでなんとも言えません。すいません。LOAD DATAとバルクインサートは用途が違うといえば違うので、あーどうだろう、えーと、まあ、検証してみたらhpのサイト(注:スピーカーはhpの人)にどんどん掲載していきますので、そちらもよろしくお願いします。
Memcached全盛な昨今ですが、まだまだ日本語での情報が不足しがちとのこと。立ち見だったのでろくなメモが残ってない。
memcached 松信 memcached pg_memcached
このメモでいったい何をどうすればいいのか。仕方ないので記憶をさぐって補足しつつ。
Facebookが15TB / 805台のMemcachedマシンを使ってるよって紹介。
memcache自体はインテリジェンスのかけらもない。APIもset、getに加えてreplace,append,prependなどなどごく基本的なものしかない。認証とかそんなもんはないし付け足す予定もさらさらない。
プロトコル(といえるほど洗練されてもいない)も単純。memcache set [key] [データ長](たとえば5)と打って、MEIJIとか入力すればmemcache get [key]でMEIJIが取り出せる。プロトコルは将来的に(速度上の理由で)バイナリベースになるかも、とのこと。
ストレージがメモリに限らなくなるだったか何か忘れたけどそういうmixiの提案が通ったことで、事情はもうちょい複雑になっていくかもしれない。
PerlやRubyやPHPやApacheやNginxやMySQLやPostgreSQLとのバインディングを紹介。mod_memcachedとpg_memcachedが気になった。でもmod_memcachedって知らないけどたぶん静的ページのキャッシュだろうから、それほどキャッシュする必要もないんじゃないかとちらっと思った。アプリがからむならアプリ側でキャッシュしてそうだし。mod_cache_disk(だっけ?)の選択肢が増えるみたいなことなのかな。どうなんだろう。
pg_memcachedとかMySQLとか、データベースのキャッシュをmemcacheでってのはなんか期待してしまうなあ。クエリキャッシュがスケールするなんて素敵すぎる。
あとウェブアプリのセッションをDBじゃなくてmemcacheで、って話をいまかいまかと待ってたけど出てこなかった。残念。
]]>eeexubuntuとか、EeePC完全対応を謳うMandrivaとかを試しても有線LANを認識しない。いろいろググったけど、結局見つけた情報はぜんぶEee PC 70x用のものばかりだったと判明。この外人のいうとおりにしたらあっさり解決した。build-essentialは特に要らなかった。
とは言っても外付け光学ドライブはあるもののUSBメモリが家にないので、プリインストールされてるXPでダウンロードとかをすることにした。
とりあえずこれでタスクバーにあるネットワークアイコンがくるくる回り出してデバイス認識+接続完了。
sudo apt-get update && sudo apt-get upgradeするとカーネルがアップグレードされて、再起動するとまたEthernetなんとかがないって言われるけど、同じ手順で復帰可能。
]]>
<?php
ob_start();
?>
X / _ / X < :来週も見てくださいね!
<?php
echo str_replace("X / _ / X < :","ひだまりスケッチx365 ",ob_get_clean());
ごめんなさいごめんなさい。演算子オーバーロード以前に、行末に;がないのと定数にはスカラしか入らないのとで、何かを表示するにはこれしか思いつかなかったんです。
でもこれPHPらしいと思うんですよ。
]]>環境は特殊といえば特殊なかんじ。ミーハーなので仕方ないですね。
]]>これはたまにじゃなくて常に。
大文字小文字は無視されるのでどっちみち検索結果は同じ。Shiftキー押下が面倒なので基本的に大文字は入れない。
delisiosとか。
Deliciousに行きたいんじゃなくて、Deliciousの綴りを調べるのに使う。カタカナ化してる英語は英辞郎(これも「えいじろう」で検索してもしかしてをコピペした)で見つかりますけどね。
例がニッチすぎて恐縮ですが、たとえばrubyのO/Rマッパ「Sequel」について調べたいのに、「sequel」だと「SQL」が同義語とみなされて関係ないものまでヒットして鬱陶しい。
「"sequel"」とすると同義語が展開されない。
↓
変換とIMEオンオフが不要なので、「1ドルin円」より「$1 in yen」のほうが入力しやすい。
でも「65536を漢字」のように日本語でないとダメ(in japaneseとin kanjiの2つで調べただけだけど)なものもある。
たとえば、mixiは海外でどう見られてるのか調べたいとき、普通に検索すると日本語情報ばっかり引っかかって邪魔。そういうときに使う。そういうときじゃなくても、日本語の記事1つとそれへのソーシャルブックマーク数十、みたいにまともな情報がないときにも使う。
操作としてはCtrl+L End &hl=en Enter。オプション検索をクリックするのは面倒なので最後の手段。
http://www.google.com/search?client=opera&rls=en&q=mixi&sourceid=opera&ie=utf-8&oe=utf-8
↓
http://www.google.com/search?client=opera&rls=en&q=mixi&sourceid=opera&ie=utf-8&oe=utf-8&hl=en
「そろそろ*一言」も考えましたが、「そろそろ*について」で充分絞り込めてるようなので広く取りました。
Wikipediaとメガフレアの2強にエビドリアが追随してて新興勢力もちらほらいる、って状況が概観できます。関係ないけどこのまえカラオケに行ったとき「青山テルマ/そばにいるね」って文字列を見て「誤植?」「山にいるね、だよな、たぶん」とか4秒ほど混乱しました。
AutoPagerize以前は100件表示でしたが、AutoPagerize + 10件表示にしてからレイテンシが向上したように思います。ていうかテクニックではないですね。
]]>User
| id | integer |
| name | string |
| created_at | timestamp |
Post
| id | integer |
| user_id | integer |
| title | string |
| created_at | timestamp |
なテーブルで、Post.user_id=User.idな関係。
class User < Sequel::Model set_schema do primary_key :id string :name timestamp :created_at,:default => :now[] # ハマりどころ end has_many :posts # 複数形 end class Post < Sequel::Model set_schema do primary_key :id integer :user_id string :title timestamp :created_at,:default => :now[] # ハマりどころ end belongs_to :user # 単数形 end
:now[]なんてのは初めてみる。これが:nowとか"now()"とかだとなんか上手くうごかない。
Modelはテーブルの1行に相当する感じ。
# --------------
# Model
# --------------
p User[{:id => 1}]
#<User @values={...}>
p User[1]
#<User @values={...}>
p User[1].name
# id=1なユーザーの名前。
p User[3] == User[:id => 3] # データ的にも意味的にもtrue
DatasetはModelをいっぱい持ったEnumableで、to_sするとSQLが見える。
# --------------
# Dataset
# --------------
p User.filter(:id => 1)
#<Sequel::Postgres::Dataset: "SELECT * FROM \"users\" WHERE (\"id\" = 1)">
p User.filter(:id < 5)
#<Sequel::Postgres::Dataset: "SELECT * FROM \"users\" WHERE (\"id\" < 5)">
p User.filter(:id < 5).order(:id)
#<Sequel::Postgres::Dataset: "SELECT * FROM \"users\" WHERE (\"id\" < 5) ORDER BY \"id\"">
p User.filter(:id < 5).first
# (おそらく)id=1なUser Model
User.filter(:id < 5).each{|u|
# ここでの u は <User @values={...}> なModel
puts u.name
}
p User.filter(:id < 5).map{|u| u.id}
# [1,2,3,4]
p User.filter(:name.like('foo%'))
#<Sequel::Postgres::Dataset: "SELECT * FROM \"users\" WHERE (\"name\" LIKE 'foo%')">
p User.reverse_order(:created_at).paginate(1,5) # ページ数,件数
# "SELECT * FROM \"users\" ORDER BY \"created_at\" DESC LIMIT 5 OFFSET 0"
# ActiveRecodeでいうwith_scopeみたいなもの
User.subset(:kiriban,:id => [100,1000,10000])
User.kiriban
# #<Sequel::Postgres::Dataset: "SELECT * FROM \"users\" WHERE (\"id\" IN (100, 1000, 10000))">
# eagerするとDatasetが持つ各Modelにメソッドが定義される感じ?
p User.filter(:id => 3).eager(:posts).first.posts
# [#<Post @values={...}>,#<Post @values={...}>, ..]
# ↑配列で返ってくる
# belongs_toも同じ。
p Post.filter(:id => 7).eager(:user).first.user
# #<User @values={...}>
# ↑配列じゃなくてハッシュ
# あわせわざ
User.kiriban.filter(:name.like('bar%')).reverse_order(:created_at).eager(:posts).all.each{|u|
puts u.posts # [#<Post @values={...}>,<Post @values={...}>]
}
てな感じ。
gemでいろいろ入れる。aptでSQLite3とかもいれとく。
# sudo gem install ramaze sequel erubis thin # ramaze -v Ramaze Version 2008.06, on ruby 1.8.6 (2007-09-24) [i486-linux]
# ramaze --create bbs # cd bbs
前から気になってたSequelを使う。MerbやRailsみたいにdatabase.ymlとかないので生で扱う。
# vim model/post.rb require "sequel" DB=Sequel.sqlite "sqlite.db" class Post < Sequel::Model set_schema do primary_key :id varchar :name varchar :title text :body timestamp :created_at end before_save do set(:created_at => Time.now) if columns.include? :created_at end end Post.create_table if !Post.table_exists?
ほぼfeatures:ormsのまま。
modelディレクトリ内にあるファイルは自動で全部読まれるみたい。最初からやり直したくなったらsqlite.dbを消せばいい。
Erubis使う。他にもいろいろ使える。Hamlは公式サイトのロゴが怖いというか気持ち悪いので使いたくない。
# vim view/index.rhtml hello <%== @title %>
最初index.html.erbとかindex.erbとかやって動かなくてハマった。
コントローラがfoo、アクションがbarなら、view/foo/bar.rhtmlかview/foo__bar.rhtmlになるみたい。
# vim controller/main.rb
class MainController < Ramaze::Controller
engine :Erubis
def index
@title="ramaze"
end
end
だいたいいつもどおり。features:templatesはあまり参考にしなかった。
# ruby start.rb
んでlocalhost:7000を見てみる。ちゃんと動くのを確認したら一旦Ctrl+C。
controller/main.rbに書く。
# vim controller/main.rb
class MainController < Ramaze::Controller
engine :Erubis
def index
@title="ramaze"
db=Post.new({
:title => "test title",
:body => "test\nbody",
:name => "ramaze test"
})
db.save
@posts=Post.reverse_order(:created_at).all
end
end
viewも変える。
# vim view/index.rhtml
<h1>hello <%== @title %> </h1>
<% @posts.each{|p| %>
<p>
<strong><%== p.title %></strong>
<em><%== p.name %></em>
<%= p.body.gsub(/\r\n|\r|\n/,"<br />\n") %>
</p>
<% } %>
リロードするたびに増えてくはず。
viewにformを足す。
# vim view/index.rhtml <form action="" method="post"> <input type="text" name="title" /> <input type="text" name="name" /> <textarea name="body"></textarea> <input type="submit" /> </form> .. 以下同文
controllerも変える。
# vim controller/main.rb
class MainController < Ramaze::Controller
engine :Erubis
def index
@title="ramaze"
post=request.POST
if post[:body] && post[:body].length > 0 # てきとー
db=Post.new(post)
db.save
end
@posts=Post.reverse_order(:created_at).all
end
end
これでタイトル、名前、本文を投稿できるようになったはず。
start.ruっていうRack用のファイルがあるはずなのでそのまま使う。WEBrickじゃなくてMongrelがいいとか、ポートが7000番じゃ嫌だ、とかならstart.rbのほうを適当に書き換えて使う。
# thin start -R start.ru -d
コード自動生成とかがない。これをシンプルというか貧相というかで適正がわかるのではないか。
]]>TB企画*これがないと困る!Firefox 3 のアドオンリスト :: Love & Design ::

まだそんなに使いこんでないので偏ってる。そんなに使い込んでないのに入ってるってことは、たぶん、ないと困るに近いはず。
FirefoxをOperaに近づけたところでそれならOpera使えばいいわけで、Firefoxには別のコンセプトを求めてる。そういう意味でVimperatorには期待してる。
というわけでいま入ってるアドオンはこんだけ。一覧の出力にはExtension List Dumper使った。
アプリケーション: Firefox 3.0 (2008052912) OS: Linux (x86-gcc3) - Adblock Plus 0.7.5.5 - Delicious Bookmarks 2.0.64 (無効) - Extension List Dumper 1.14.1 - Firebug 1.2.0b3 - FireGestures 1.1.2 (無効) - Greasemonkey 0.8.20080609.0 - Live HTTP Headers 0.14 - MeasureIt 0.3.8 - Nightly Tester Tools 2.0.2 - PrefBar 4.1.0 - Relaxed the HTML Validator 0.9.5 - S3 Firefox Organizer(S3Fox) 0.4.1 - Vimperator 1.1 - ツリー型タブ 0.7.2008062001 (無効)
詳細なリスト。
アプリケーション: Firefox 3.0 (2008052912) OS: Linux (x86-gcc3) 2008年6月26日 合計: 14 - Adblock Plus 0.7.5.5 http://adblockplus.org/ 広告は過去の遺物です! - Delicious Bookmarks 2.0.64 (無効) http://delicious.com Access your bookmarks wherever you go and keep them organized no matter how many you have. - Extension List Dumper 1.14.1 http://sogame.awardspace.com/ インストールした拡張(アドオン)の一覧を生成します。 - Firebug 1.2.0b3 http://www.getfirebug.com/ Web Development Evolved. - FireGestures 1.1.2 (無効) http://www.xuldev.org/firegestures/ Executes various commands with mouse gestures. - Greasemonkey 0.8.20080609.0 http://www.greasespot.net/ A User Script Manager for Firefox - Live HTTP Headers 0.14 http://livehttpheaders.mozdev.org/ ウェブページとブラウジング中の HTTP ヘッダを表示します。 - MeasureIt 0.3.8 http://www.kevinfreitas.net/pro/extensions/ Draw out a ruler to get the pixel width and height of any elements on a webpage. - Nightly Tester Tools 2.0.2 http://www.oxymoronical.com/web/firefox/nightly Useful tools for the nightly tester. - PrefBar 4.1.0 http://prefbar.mozdev.org/ A toolbar for quickly accessing and changing common preferences, run scripts and many more... - Relaxed the HTML Validator 0.9.5 http://relaxed.vse.cz/ "Relaxed" is a HTML validator which validates HTML documents using it's own schema definitions written in Relax NG with embedded Schematron patterns. - S3 Firefox Organizer(S3Fox) 0.4.1 http://www.suchisoft.com/ext/s3fox.php An user friendly interface to manage Amazon's S3 (Simple Storage Service) - Vimperator 1.1 http://vimperator.mozdev.org Make Firefox behave like Vim - ツリー型タブ 0.7.2008062001 (無効) http://piro.sakura.ne.jp/xul/_treestyletab.html タブをツリー状に表示します。]]>
というわけでZed Shawがいかしたフレームワークと呼ぶMerbをPassenger 2.0 RC1で動かしてみた。以下Ubuntu 8.04をベースに進めていきますが、適宜読み替えてもらえば他のディストリでも大丈夫だと思います。
MERB_ROOTにconfig.ruって名前のrackup用ファイルをおいとけばPassengerが勝手に読むみたい。あるいはRackBaseURIで明示的に指定っぽい。
~/# wget http://phusion-passenger.googlecode.com/files/passenger-1.9.0.gem ~/# sudo gem install passenger-1.9.0.gem ~/# sudo passenger-install-apache2-module --snip-- Please edit your Apache configuration file, and add these lines: LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-1.9.0/ext/apache2/mod_passenger.so PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-1.9.0 PassengerRuby /usr/bin/ruby1.8
/etc/apache2/mods-available/passenger.confにLoadModule....をコピペして、mods-enable/passenger.confからシンボリックリンクを張る。
とりあえず試すだけならhttpd.confにコピペしてもいい。
SQLite3とかはとりあえず入ってるものと仮定。
~/ # sudo gem install activerecord merb merb_has_flash merb_helpers merb_activerecord --snip-- ~/ # merb -v merb 0.9.3 ()
Jamie Hoover - Merb, SQLite, and Nginx on Ubuntuとかmerb.introを参考に進める。
rake dm:db:automigrateが動かなかったのでDataMapperじゃなくてActiveRecordを使うことにした。
~/ # merb-gen app testapp
~/ # cd testapp
~/testapp # vim config/init.rb
# Uncomment for ActiveRecord ORM
use_orm :activerecord
~/testapp # merb-gen resource article title:string body:text
-- config/database.ymlが無いっておこられる --
~/testapp # mv config/database.yml.sample config/database.yml
~/testapp # vim config/database.yml
---
# This is a sample database file for the ActiveRecord ORM
:development: &defaults
:adapter: sqlite3
:database: sample_development
:username: the_user
:password: secrets
:host: localhost
:socket: /tmp/mysql.sock
:encoding: utf8
:test:
<<: *defaults
:database: sample_test
:production:
<<: *defaults
:database: sample_production
~/testapp # merb-gen resource article title:string body:text
dependency model
exists app
create app/models
create app/models/article.rb
dependency migration
create schema/migrations
exists schema
exists schema/migrations
create schema/migrations/001_article_migration.rb
dependency merb_model_test
exists spec
create spec/models
create spec/models/article_spec.rb
dependency resource_controller
exists app
exists app/helpers
exists app/views
create app/views/articles
exists app/controllers
create app/helpers/articles_helper.rb
create app/views/articles/show.html.erb
create app/views/articles/index.html.erb
create app/views/articles/edit.html.erb
create app/views/articles/new.html.erb
create app/controllers/articles.rb
dependency merb_resource_controller_test
create spec/controllers/
create spec/helpers/
exists spec
exists spec/helpers
create spec/views
create spec/views/articles
exists spec/controllers
create spec/helpers/articles_helpers.rb
create spec/views/articles/delete.html.erb_spec.rb
create spec/views/articles/index.html.erb_spec.rb
create spec/views/articles/edit.html.erb_spec.rb
create spec/views/articles/new.html.erb_spec.rb
create spec/views/articles/show.html.erb_spec.rb
create spec/controllers/articles_spec.rb
~/testapp # rake db:migrate
--snip--
~/testapp # merb -p 3001
とりあえず普通にMerbが動くかテスト。http://localhost:3001/articlesを見てみて「Edit this file in app/views/articles/index.html.erb」とか表示されたらOK。
続いてPassengerの設定。Merb Wiki | Phusion Passengerを参考に。
~/testapp # vim config.ru
# config.ru
require 'rubygems'
require 'merb-core'
Merb::Config.setup(:merb_root => ".",
:environment => ENV['RACK_ENV'])
Merb.environment = Merb::Config[:environment]
Merb.root = Merb::Config[:merb_root]
Merb::BootLoader.run
run Merb::Rack::Application.new
~/testapp # sudo vim /etc/apache2/httpd.conf
<virtualhost *:80>
ServerName merb-test
RackEnv development
DocumentRoot /home/myname/testapp/public
ErrorLog /home/myname/testapp/log/error.log
</virtualhost>
~/testapp # sudo /etc/init.d/apache2 force-reload
/etc/hostsに「127.0.0.1 merb-test」を追加して、http://merb-test/が見れたら成功。
]]>Unable to create the anonymous user.
何度やってもこれが出るので、AMD64だからかとかpassengerが原因かとかいろいろ疑ったりしたけど全部濡れ衣だった。
SQLite 3.3.7だか3.3.8に非互換な変更が入ったらしく、Debian etchのAPTにあるsqliteがまさに3.3.8で死亡。SQLiteにこだわりがなければMySQLとかPostgreSQLを使えば普通に動きます。
ちなみにUbuntu 8.04の3.4.2だと大丈夫でした。とりあえずUbuntuで動かして、DebianはリバースプロクシでUbuntuを見に行くようにしました。
ていうか今みてみたら公式に書いてますね。

Alt+クリック。その他のブラウザは普通に右クリックでOK。
Operaはデフォルト設定で右クリックを制御しようとするJavaScriptを拒否します。その昔、ページを右クリックすると「右クリック禁止です」とか出てくる変なスクリプトが流行ったころの名残です。
/javascripts/context_menu.jsのソース読んでたら気づきました。Operaなんかいつも無視されるのにRedmineはいいやつだ。if (window.opera && !e.altKey) { return; }はちょっとどうかと思うけどいいやつだ。
ちなみにShift+クリックとかCtrl+クリックは、ちゃんと期待したとおり複数選択してくれます。

ガントチャートなのでそりゃそうだ、といった感じですが、案外盲点なので注意。
なお、このスクリーンショットはフィクションです。

なんか隠しキャラ探しみたいになってきた感もある。
なお、このスクリーンショットはフィクションです。モザイクは演出です。
ヘッダにある管理→設定→リポジトリの一番下にある「修正用キーワード」の設定を見直すこと。
「担当(assigned)」は、担当者が決まったのであとはよろしくって状態。「新規(new)」はとりあえず問題があったのでチケット書いたよ、って状態かな。たしかに和訳が難しい。
「解決(resolved)」はチケットに書かれてる作業をしたよ、って状態。作業内容をチェックして問題なければ「終了(close)」になる、とおもう。
とりあえず漢字2字がいっぱいあると一覧性というか視認性が悪いので、家では「new」「assigned」「作業済み」「意見待ち」「おわり」「却下」に名前変えてつかってる。優先度も「そのうち」「あとで」「通常」「高め!」「緊急!!」にしてる。
ヘッダにある管理→設定→テーマでclassicとかalternateにしてみるといいかも。
ヘッダにあるプロジェクト→右上にある「チケットを全て見る」で見れる。
会社ではapp/views/layout/base.rhtmlを編集してヘッダに常にリンク出すようにしてある。
]]>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.2");</script>
コメントがなぜか反映されないので、あと他にもどっかで2回くらい見たので書いておく。
Developer's Guide - AJAX Libraries API - Google Code
Each library is available via both google.load(), and directly via <script/> tag.
と書いてあるように、各種ライブラリは<script />からダイレクトに呼び出せる。
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"></script>
でいい。URLの1.2.6を1.2とか1とかにするとワイルドカード指定みたいになる。
というかgoogle.load()がやってることは、このURLを作って<script>にセットしてappendChildしてる感じ。
Expiresがあるので、どっかで一度でも同じURLのスクリプトを読んだことがあってキャッシュが生きてたら、サーバへのリクエストが発生しないのでGoogleが落ちててもまったく問題ない。
]]>PostgreSQLだけど。MySQLはサブクエリが微妙だったはずなので出来るかわからない。
使用したバージョンはPostgreSQL 8.2ですが、それほど新しくないPostgreSQLでもいけると思う。
Kazuho@Cybozu Labs: フレンド・タイムライン処理の原理と実践
1. フォローしている各ユーザーについて、そのメッセージ ID の最大値を取得
2. 1 のリストをメッセージ ID の降順でソートし、その先頭 20 件 (1ページ分) 以外を破棄
3. 2 のリストの全ユーザーについて、最新 20 件のメッセージを取得し、マージ
4. 3 のリストの先頭 20 件が、クライアントに返すべきフレンド・タイムライン
テーブル定義はこんな感じ。serial型はinteger AUTO_INCREMENT(と同じとみなしていい)。
CREATE TABLE follower ( id serial NOT NULL, followed_user_id integer NOT NULL, -- フォローされてるユーザID following_user_id integer NOT NULL,-- フォローしてるユーザID CONSTRAINT follower_pkey PRIMARY KEY (id) ); CREATE TABLE messages ( id serial NOT NULL, user_id integer NOT NULL,-- 発言した人のユーザID message character varying(255) NOT NULL,-- メッセージ本体。character varying=varchar CONSTRAINT messages_pkey PRIMARY KEY (id) );
データを入れていく。user_id=1の人のフレンドタイムラインを構築する予定。
-- user_id=1の人が、user_id 2〜19までをフォローする。 -- 特に19である必要も連番である意味もない。 INSERT INTO follower (following_user_id,followed_user_id) VALUES (1,2) ,(1,3) ,(1,4) ,(1,5) ,(1,6) ,(1,7) ,(1,8) ,(1,9) ,(1,10) ,(1,11) ,(1,12) ,(1,13) ,(1,14) ,(1,15) ,(1,16) ,(1,17) ,(1,18) ,(1,19); /* |following_user_id|followed_user_id| |1|2| |1|3| |1|4| |...|...| |1|19| */ -- 以下のようなSQLを最低でも1000回くらいは繰り返してダミーデータ生成。 INSERT INTO messages (user_id,message) VALUES (floor(random()*50)+1,random()::text); -- user_id 1〜50の発言をランダムに生成
準備が出来たらいよいよ取得。user_id BETWEEN 2 AND 19じゃないやつが出たら失敗。
select main.* from messages as main inner join ( select user_id from messages where user_id IN ( select followed_user_id from follower where following_user_id=1 -- user_id=1の人のフレンドラインを取得するので。 ) group by user_id order by max(id) DESC -- 手順1:メッセージID最大値 limit 20 -- 手順2:21件目以降は破棄する ) as tmp on (tmp.user_id=main.user_id) -- 手順3:マージ order by id desc limit 20 -- 手順4:結果20件
うまくいった(はず)。
ちなみに2回出てくるlimit 20と、following_user_id=1がそれぞれ変数になります。
]]>カメラ知識は皆無なのでいろんなパラメータの挙動が知れておもしろかった。
途中で「絞り値」と「F値」というのが出てきた。両者は相関しているらしい。表で書くと
| 絞り値 | 0 | 1 | 2 | 3 | 4 | … |
| F値 | 1 | 1.4 | 2 | 2.8 | 4 | … |
という関係。どう見ても1.4と2.8が怪しいので一般式へとリバースエンジニアリングしてみたらf(n)=sqrt(2^n)が導き出された。変に小数にせず
| 絞り値 | 0 | 1 | 2 | 3 | 4 | … | n |
| F値 | √1 | √2 | √4 | √8 | √16 | … | √(2^n) |
って書いたほうがわかりやすかったのでは。
全体を通して、絞り値を増やすとこう、減らすとこう、シャッタースピードがこうだとこういう風に写るからこうしよう、といったように実例やシチュエーション付きで説明されたのですごく頭に馴染んでいった。
あと発表資料の中で「恋するタイマー」ってのが出てきたので、一緒に行った人と「恋するタイマーって何?」「マジで恋する5秒前ですよ」「あー」とかいう会話をした。
さすがのアップルでもプリンタはhp製品なんだなー、とか目の前のプリンタを見ながら考えてた。そりゃいち早くFDDを取っ払ったり光学ドライブを取っ払ったりする先進的なジョブズだから紙なんて興味ないのかもしれない。
アップルはプリンタつくらないんですかって訊いたら、「紙にインクを吹きつけて一体何がしたいんだ? もし偽札をプリントしたいのなら、まずはペイントするべきだ。Macは後者の仕事をフルサポートする」とか反論しながら営業されそうだ。やるなあ妄想ジョブズ。
とか考えてたらいつの間にか終わってた。
CSS NITEでプログラムの話はできないなあと思った。プログラムの話はプログラムの話ができる場所でするべきか。テーマがデザイナーとの協業みたいな感じだったので、悲惨なまでに背理法だなとますますいたたまれなかった。誰が悪いわけでもない。
ずっと立ちっぱなしで腰とかが痛かったのでそのままiPod関連商品を見るなどして帰宅した。Macの値段を見るの忘れた。
]]>革命の日々! ITProのLinuxチューニングの記事がひどい事になっている件について
などのようにITProは地雷に満ちているので、注意するようにちゃちゃっとスクリプト書いた。

5秒経つと消えます。クリックすると消えます。Firefox/Opera両対応。
検証不可能性にかこつけてでたらめ言いたい放題だからだと思うよ。
あるいはてきとうなことばっかり言うからだとおもうよ。
W3Cの勧告に従うとか、Web標準に準拠するとか、XHTMLでサイトを作成するとか。これだけでも効果はバツグンである。
W3CやWeb標準に則っていないサイトや、Internet ExplorerやFirefox、Safariでほぼ同じに表示されないページ、見出しや段落、リストで構造化されていないコンテンツは、どんなに内容が深くても、良いとは言えないのである。
W3CはXHTMLのContent-Typeにapplication/xhtml+xmlを推奨してますが、そうするとIEでは表示すらされません(ダウンロードダイアログが出ます)。
あと十把一絡げにIEとかSafariとか書いてますが、IE5.xとかSafari2以前も含めてちゃんと表示させるには、標準を捨ててtableでレイアウトするのが「ほぼ同じに表示」するための唯一の道です。
前段と後段は方向性が逆ですから、主張として矛盾しています。
見出しと段落を使いこなす、結論を用意し論理の展開をキッチリする。そういう論文作成の手法をマスターすれば、SEOの70%は達成できる。
GoogleもYahooも文意や論理までは解析できません。出来るならワードサラダスパムはとっくに絶滅してるはずです。理解できないものは評価できませんから、検索結果にはほぼ影響しないと考えるのが妥当です。
もちろん副次的には影響します。「ほぼ」と表現したのはこれのためです。しっかりした文書は人間のためのものですし、価値の高いリンクを張るのも人間です。検索エンジンはそういうプロセスをページとリンクといった単位でとらえ、追いかけているに過ぎません。
あるいは同じキーワードを含むにしても、地の文よりも見出し、見出しよりもタイトルを重視するのは理にかなっています。地の文しかなく見出しやタイトルがなければ、検索エンジンとしても重み付けのしようがありません。
しかしそれと文書全体の評価とは別です。これは検索されたキーワードと解析済みの文書との関連性を探るには有用なアプローチですが、タイトルにキーワードが含まれているからといって文書全体が高品質だと判定するのは浅はかに過ぎます。なお、今のところ文書の品質は被リンクや更新日時(新しさ、あるいは古さ)によって計られているようです。
では具体的には何をすればいいのか。明白です。検索エンジンがより理解しやすいように体裁を整え、「検索エンジンが考える高品質な」ページをつくるべきです。
すべての文書にはパーマリンクを用意し、Googleウェブマスターツールなどを使ってページの存在を通知する。?keyword=aと?keyword=a&sort=dateと?keyword=a&sort=price、あるいはxxx.htmlとxxx.html?from=rssのように、内容がほぼ同じページが大量にあると検索エンジンは重要性をうまく判定できないので、robots.txtやmetaタグで不要なページをインデックスしないように気遣う。もちろん内部リンク網も気にしないといけません。これらはすべて人為的におこなえますが、まだ最低限の施策です。
準備が整ったら、次はもちろんページをつくる必要があります。質はリンクと更新日時によって計られますから、更新日時はともかくまずはリンクを獲得することを考えないといけません。
ここでSEOだけしか考えていないと以下のような無限ループに陥ることになります。
「リンクを獲得しないと検索エンジンで人に見つけてもらえない」
↓
「まだ立ち上げたばかりだから誰も知らない」
↓
「誰も知らないからリンクが獲得できない」
↓
「リンクを獲得しないと人に見てもらえない」
このジレンマを力業で強引に解決するのが、俗に言うSEOスパムです。数万円もはたいてくだらないツールを買い、あちこちにコメントスパム、トラックバックスパム、ソーシャルブックマークスパムを繰り返すことで、なるほどリンクは獲得できます。
このスパム行為をSEOだといってはばからない、あるいは前述の無限ループを挙げてこうしなきゃどうしようもないと開き直る、言い分は様々ですがスパムはスパムです。Google先生がスパムだとおっしゃっておられるのでスパムです。
検索エンジンにバレたら、あるいはチクられたら申し開きも出来ないまま検索結果から削除されて終わりです。自称小遣い稼ぎのアマチュアスパマーならその後なんとでもできますが、商売でやってるサイトがドメインごと削除されたらなかなか一大事といえるでしょう。
正直なところ、このブログを作る前は僕も上のようなジレンマに陥ってるふしがありました。いちばん最初の集客というか、発見されるという状態がどうやって起こるのか。頭ではわかっていても経験として持ってなかったので、SEOと称してスパムする人の言い分にも一理あると思っていたのです。はてなダイアリーのように内部リンクが濃密で、キーワードページなどによる発見機会もふんだんにあるならばわかるのですが、孤島に等しい独自ドメインでやる場合はどうなんだろう、と。
とりあえずブログをつくって、更新したらGoogleやテクノラティに更新Pingを打つくらいはしましたが、それだけでもGoogleからちらほらと(マイナーなキーワードでですが)アクセスはありました。他にもサイト構造を考えるとか細々としたことをやりましたが、あまり考えても埒があかないので(というか面倒なので)ふつうにブログすることにしました。すなわち言及リンクやトラックバックを活用してなんかいろいろと書く行為をしばらく続けました。
トラックバックからのアクセスはあまり多くありませんでしたし、たいていnofollow付きリンクですから被リンクという意味では効果も薄いですが、わざわざトラックバックをたどって読みに行く人というのは情報感度の高い人です。そういう人がはてなでブックマークを付けてくれると、たまにそこから2users、3usersとなって注目のエントリーに表示されるようになると、さらに4users、5usersと膨らむこともあります。
この流れがうまくいくかどうかはSEOとかを抜きにした対人間の意味でのページの質、話題性というか、そういうものによってのみ左右されます。検索エンジンの出番はまったくありません。
そうこうするなかで何の前触れもなくConnection refused - connect(2)が30users以上になりました。特にトラックバックとかも飛ばしてないんですが、どうしてこうなったのか未だに因果関係がよくわかりません。
最初は「ニコニコ動画」キーワードを追いかけてるkoizukaさんのブックマークから波及したのかな、と思ったものの順番的にそうとも言えないっぽいですし。他に考えられるのは「ニコニコ動画」を含むエントリーを自動で収集し続けるニコニコ動画まとめwiki - ニコニコ動画まとめwikiからかな?
何にしろ、それなりに知識があると思ってた僕が予期しなかったかたちでアクセスがいっぱい来ました。その中にはフィードを購読してくれる人も何人かいらっしゃいましたから、発見される機会が今までと比べて大幅に増大したことになります。ちゃんとした記事を書けば、ちゃんと見てくれる人がいてくれるわけです。それにNicokitのときのようなことがまた起きるかもしれません。こうなるとSEOとかもうどうでもいいですね。
商売ならadwordsやオーバーチュアに広告を出したり、リンクシェアやA8と提携したりして、露出する方法はいくらでもあると思います。ネットに限らなければそれこそ雑誌や中吊り広告やビラなど、いくらでもあります。別にみんながみんな検索エンジンの検索結果をクリックして来るわけではありません。
広告の予算が採れないからタダないし格安で出来るSEO、というのはわからなくもないですが、スパムは先に書いたようにインデックス削除のリスクが高いですし、相互リンクのお願いとかしても検索結果に対しては暖簾に腕押しだと思います。コストパフォーマンスが悪すぎます。
あるいは海外のように、大量のブックマークを意識的に獲得するのもSEOといえます。上に書いたNicokitのような現象をわざと狙うわけです。極端にいえば、なんとかメーカーを作るとかですかね。これは別にスパムではありません、というか、Googleも勧めています。
で、広告なりSEOなりスパムなりで集客して何がしたいのかというと、当然ながら商品販売であったり見積りゲットだったりだと思います。にもかかわらず、売上やコンバージョン率よりも検索結果の順位にこだわる人はそれなりに居ます。そういう人はもう手遅れなので、遅かれ早かれスパムに手を出して自滅するか、利益をスパムツールの購入にあてるとか、日がな一日検索結果の浮沈に一喜一憂するなどしてがんばってください。
こういった原則を一切説明せず、キーワード比率だのmetaタグだのといったオカルトじみた情報を小出しにするという霊感商法みたいなことをやってるからだと思いました。
]]>
コインランドリーは稼働開始から45分とか50分経ったら乾燥機へ移しかえ、さらに45分だか50分待って回収しないといけない。時計を見て確認するのは開始時刻の記憶と計算が面倒だから、ぜひともタイマーが必要だ。
45分後にタイマーをセットしても、45分後は忙しかったり気が乗らなかったりしてスルーするかもしれない。スヌーズつけてても同じ。スルーしたら当然わすれる。タイマーの意味がない。タイマーが鳴ったら忘れないうちに取りに行かないといけない、という覚悟はそのまま負担になってこれまたメモの意味がない。
そもそもきっちり45分である必要がまったくないのにタイマーはどいつもこいつもパンクチュアル過ぎる(パンクチュアルって一回いってみたかったのを達成)。
かといって何もしないと忘れる。また、メモは目につくところにないと存在自体をわすれるので意味がない。
というわけで、
としてtwitter(ていうかtwippera)が最適だという結論に至りました。「N minutes ago」って具合に時間を大雑把に表示してくれるのがミソです。followが少ないなど、1時間くらい経っても自分の発言が視界に入ることが条件ですが。
などのように、一分一秒をあらそわない場合にはtwitterのゆるさがフィットすることかと存じます。
普通の文章を書くだけなら有用かもしれないけど、コードとか"とかをいっぱい使うものばっかり書いてるので、さすがにちょっと邪魔されすぎだと感じた。
Markdownとかもちらっと見たけど、なんかPHPタグの扱いがどうとかなんやかんで面倒そうなのでやめて見なかったことにしてText::Hatena README (ja)を使わせてもらうことにした。gemファイルをwgetしてsudo gem installで準備完了。ブログのほうをちょっと手直ししてあれこれして完了。のはず。
あと入力したコードやはてな記法をそのまま表示する(スーパーpre記法) - はてなダイアリーのヘルプの記法で<とかが無修正だったので、parse後に無理やり実体参照にした。
それにしても[http://example.com:title]で勝手にページのタイトル取ってきてリンクにしてくれるのはすごく便利だなあ。
]]>Minibufferはオーバースペック みたいなことを書いておきながら、やっぱり「b」「B」でブックマークできる手軽さがうらやましい。Opera移植版 がなぜか動かなかったので自作することにした。自分のためだけに作ったので極めて低機能。
minibuffer-bookmarkcommand-delicious.js
ファイル名からもわかるようにdel.icio.usにしか投稿できません。あと他のコマンドとパイプでつなぐとかLDRizeと連携したりはできません。Opera移植版Minibuffer に依存しています。Firefoxで動くかはわかりませんけどたぶん大丈夫です。
--tags」でインタラクティブにタグをつけてポスト(補完付き)--tags=a」「'--tags=aa bb cc'」でタグ指定してポスト--comment」でコメント付きでポスト--comment=おいしそう」でコメント付きでポストbookmark-current-delicious --tags=testソースコードの上のほうにあるusernameとpasswordを設定したら、上の「できること」ができるようになる。
これくらいなら自分で専用のやつを作った方がいいんじゃないかと思いましたけども、補完付きプロンプトや表示、キーバインド処理なんかをMinibuffer任せにできるので楽かなあと思って。
]]>というようなものがあったけど、ニックネーム付きブックマーク で解決した。Greasemonkeyのユーザースクリプトコマンドに似てるような似てないような感じ。
deliciousにポストするのはShift+F2 dで、tumblrへはShift+F2 tでいける。実際にはtumblrってニックネームにしてるけど、tで始まるニックネームが他にないので勝手に飛ぶ。あとShift+F2はちょっと押しづらいので住み慣れてきたら違うキーに設定する予定。
管理はブックマークの管理をそのまま流用できて楽。bookmarkletフォルダ作ってそこに全部いれておけば、インクリメンタルサーチと編集と削除と(Opera Linkを使えば)同期とバックアップまで出来て文句ない。
Minibuffer がちょっと豪華過ぎるので、とりあえずこれでしばらく過ごしてみよう。
]]>セキュリティが便利さを生贄にすることで得られるのと同様に、高速化はコードの可読性や保守性と引きかえになる。よっぽどまずいコードでなければ。
63個もあってこれがないのかよ、っていうのはこんな感じ。
?>をつけるな(ref. Zend Frameworkコーディング規約)==じゃなくて===使おうぜprintをechoにしてみたりするのは、メラよりヒャドのほうがダメージ高いとかいってゾーマに連発するようなもの。とっとと勇者にバイキルト掛けるべき。パーティ編成を見直すべき。戦略を考えるべき。どうしても無理ならレベル上げるべき。
01.static にできるメソッドは static として宣言しよう。(4倍速い)
$this->method()とself::method()じゃ意味が違います。self::method()だと$thisを無視できるので理論的には軽くなるけどどうでもいい。
02. echo の方が print より速い。
インデントをやめて改行も消したらより速くなるよ。
03. echo ‘文’,’字’; (カンマ区切り)の方が、’文’.’字’ (ドット連結)より速い。
.だと文字列を連結してからecho、,だととりあえず頭からechoしていく、の違い。ドットは連結した文字列を格納するから遅い。たぶん。どうでもいい。
04. ループの最大値は、ループ「内」ではなく「前」にセットしておこう。
19. for 文の条件式には count($array) のような関数をいれない。(変数に格納)
47. ループ処理には foreach を使おう。
配列のループにはforeachを使うべき。 forの出番はまずないし、あったとしてもfor($i=0; $i<10; $i++)のように数値リテラルでループ回数指定するくらいだろうからどうでもいい。
05. 大きい配列のような変数は unset() してメモリを解放しよう。
大きいって具体的にどれくらい?
最近だと気にしなくていいのかもしれないけど、循環参照の場合はunset()するようにはしてる。
06. マジックメソッド(例:
__get, __set, __autoload)は使用を避けよう。
ついでにクラス定義とかrequireとかもやめて1ファイルで完結しちゃえばもっと軽くなるよ。
07. require_once はハイコストなのです。
52. require_once より require を使おう。(opcode キャッシュがらみの理由で)
require()だと同じクラスや関数を2回定義しようとしてWarningかなんかが出る可能性があるよ。人力でチェックするのとどっちがexpensiveかは状況によるかも。
数マイクロ秒 vs. 人間の神経って図式なので、数マイクロ秒のほうが重要なら人間性を無視する方向で。
08. include や require でファイルはフルパスで指定しよう。
51. ファイル指定は basename / file_exists / open_basedir よりフルパス指定が速い。
require_once dirname(__FILE__)."/foo.php";くらいならいいけど、PEARまでフルパスっていわれるとちょっと。
フルパスで指定するのは曖昧さ回避のためにするべきで、パフォーマンスのためにやるべきじゃない。
ていうかbasenameでファイル指定?
09. スクリプト開始時間は time() でなく $_SERVER[‘REQUEST_TIME’] で取得。
PHP5から。互換性のためにスクリプト冒頭で代入してしまうのも豪快でいいかもしれない。
10. 可能であれば、正規表現より strncasecmp、strpbrk、stripos を使おう。
正規表現よりその3つを使い分けるほうが難しいんじゃないだろうか。
11. strtr(str_replace の4倍速い) > str_replace > preg_replace の順に速い。
strtr() / str_replace()だと正規表現じゃないんだな、というメッセージは受け取れます。
12. 引数に配列、文字列両方を受け入れるような関数は避け、個別に関数化しよう。
ん? こういうコードのことを言ってるんだろうか。
function sample12_before($var){
if(is_array($var)){
// なんか処理 for 配列
}elseif(is_string($var)){
// なんか処理 for 文字列
}
}
を
function sample12_after($var){
if(is_array($var)){
some_func_for_array($var);
}elseif(is_string($var)){
some_func_for_string($var);
}
}
にしようってことかな。必要な状況がよくわからないけど、(array)$varでキャストしたらダメ?
13. if をたくさん使ってるなら switch を活用しよう。(訳注:ってことでいい?)
訳注とあったので原文見てみた。「It’s better to use select statements than multi if, else if, statements.」って、selectステートメントってなんやねんってことか。なるほど。
elseif連発よりswitchのほうが好ましいのでこれはあり。
14. @によるエラー制御はすんごく遅い。
この前かいた問題 。これもパフォーマンスでなく可読性とかエラー制御とかそういう観点から述べるべき、というのが僕の考え。
15. Apache の mod_deflate(Apache2系) を ON にしておこう。(No.42 参照)
42. Apache の mod_gzip(Apache 1系)は、転送量を最大80%減。
なんで急にHTTPサーバの話に。
とりあえずgzipに関してはみんな大嫌いなIEが例のごとく腐ってる ので留意すること。
16. 処理が終わったらデータベースの接続は切っておこう。
昔while()ループの中で毎回connectとdisconnectを繰り返すプログラムがありまして、実に大変なことになっていたことがあります。
軽くなるわけでもないし、意識的にdisconnectしなきゃいけないことは滅多にないかと思います。
17. $row[‘id’] は $row[id] より7倍速い。
後者の場合、PHPは「id」という定数が定義されてるか調べて、もし定義されてなければ「id」という文字列を返す、という処理をします。
後者のこの書き方は非推奨です。
18. エラーメッセージはハイコスト。
そうか! エラーメッセージを組み込まなければいいんだ! んなアホな!
20. メソッド内ではローカル変数をインクリメントするのが一番速い。
比較対象はなんだ。どうしろっていうんだ。$tmp=$this->foo;$tmp++;$this->foo=$tmp;しろとでもいうのか。
21. グローバル変数のインクリメントはローカル変数より2倍遅い。
グローバル変数……。インタプリタ的にローカル変数のほうがグローバル変数より手近だから。
22. オブジェクト変数(例:$this->v++)のインクリメントはローカルより3倍遅い。
たぶん理由は上に同じ。
23. 未定義のローカル変数のインクリメントは定義されたものより9~10倍遅い。
24. 未定義のグローバル変数についても同様。
そりゃ定義する必要がありますから当然です。あとE_NOTICE出ます。
25. メソッドの起動コストはクラス内のメソッド数とは無関係。
さすがに内部ではO(1)な実装にしてるでしょう。
26. 派生クラス内のメソッドは、基底クラスで定義されたメソッドよりも速い。
理由は21,22あたりと同じ。
27. 引数1つの空関数を呼び出すのにも、ローカル変数 $local++ 7~8回分のコスト。
そうですか。
28. ダブルクォート より シングルクォート の方が若干速い。
意味が違う。シングルクオートなら変数展開しない。
29. 03 と重複か。(注:これは複数の引数を受け取る echo でのみ有効)
らしいので03参照。
30. PHP スクリプトは静的な HTML ページよりも2~10倍の時間がかかる。
そりゃmod_php読んでスクリプト解析して実行するんだから重いに決まっています。
31. キャッシュを利用しよう。通常、コンパイル回数 x 25~100% 分速くなる。
コンパイルキャッシュのことかな。数値の根拠は不明だけど入れて損はないと思う。
32. これもキャッシュについて。memcached とか使おうよ。
今までのを読む限りmemcachedって言いたかっただけじゃないかと思ってしまう。memcachedはいいものです。
33. if (strlen($foo) < 5) を調べたいなら if (!isset($foo{5})) と書くと速い。
たしか後者の書き方はPHP5の時点で非推奨になってたはず。あとマルチバイトだと期待どおり動かない。百害あって一利なしかと。
34. $i++ より ++$i の方が速い。(そういう opcode optimizer が走ってる場合)
「そういう opcode optimizer が走ってる場合」を使えばなんとでも言える。
35. 全部が全部 OOP でなくても良い。(コストやメモリの浪費になってるかも)
それはそのとおり。カッコ内は微妙だけど。
36. すべてのデータをクラスにしようとしないこと。配列も十分便利です。
配列とクラスはまったく別物なんですが……。
37. メソッドを分割しすぎない。(どれを再利用するのかよく考えよう)
これもだいたいそのとおりだけど、関数呼び出しのオーバーヘッドって意味なら同意できない。
38. メソッドを分割したくなったら後でいくらでもできる。
実際に(気力的な観点から)やるかどうかは別として、これもそのとおり。
39. もとから用意されてる 関数たち を活用しよう。
これもそのとおり。そのほうが速いしバグもないし読みやすいから。
40. 非常に時間のかかる関数 → C言語による拡張モジュール化も検討しよう。
これもそう。重いorPHPだとめんどいなら他の言語のパワーを積極的に使うべき。
41. コードをプロファイリングしてボトルネックを見つけよう。Xdebug とか。
最適化の基本中の基本。プロファイリングもせずに最適化とか無茶。
43. : A HOWTO on Optimizing PHP with tips and methodologies を見てね。
見た。We summarize the optimization methods below:って書いてある表が、流れと効果がわかりやすくていいと思う。
48. 複雑なクラスを作ってるなぁと思ったら → Singleton モデルを検討しよう。(原文:Do consider using the Singleton Method when creating complex PHP classes.)
訳者の人も意味がわからず混乱中みたい。僕も意味がわからない。
たぶん、同じクラスを何回もnewするよりsingletonでインスタンスを使いまわせば(ミクロな意味で)速くなる、って言いたい、の、か、なあ。
おそらく無意味だから無視していい。
49. DB に入れる値なら GET より POST を使おう。(パフォーマンスが上がる)
HTTPのごく基本的なところだけでも勉強してください。
50. 可能なら正規表現より、ctype_alnum、ctype_alpha、ctype_digit を使おう。
それぞれ英数字のみ、英字のみ、数字のみのときにtrueを返す関数。
正規表現だと^とか$をつけ忘れたり、[0-9]*のように+じゃなくて*とかやってしまったりするかもしれないし、安全でいいと思う。パフォーマンスはどうでもいい。
53. 一時的なファイルを作るときには tmpfile や tempnam を使おう。
39と同様。
54. 外部サービスに XMLHTTP で接続するときにはエラー回避のため Proxy を使おう。
XMLHTTP? JavaScript? 外部に対して??
何にしろProxyでキャッシュしとけば安全な気はする。Proxyがその外部のサービスより安定してれば。
55. デバッグするときは error_reporting (E_ALL); にしておこう。
個人的にE_NOTICEとは決別しました。基本的にE_ALLでいいと思う。
56. Apache の allowoverride を “none” にしておくとパフォーマンスが向上。
PHP関係ない。そんなの言うくらいならApacheにももっといっぱいあるだろう。
57. 静的なコンテンツには thttpd のような高速なファイルサーバを使おう。
リバースプロクシろうぜ的な。
58. パスなどの設定パラメータは配列に serialize して入れ、キャッシュしておこう。
毎回parse_ini()するかunserialize()するかの2択ですね。わかります。
59. アクセスの多いページには PHP の 出力バッファリング を使おう。
ob_* な面々について。ついでにキャッシュしとけばなおよし。
60. DB 固有の prepare メソッドではなく PDO::prepare を使おう。
PHP5から。特にいうことなし。
61. SELECT * (ワイルドカード)を使うのはやめよう。
JOINとかGROUP BYとかがからむならまだしも、毎回selectのキーを指定する処理のほうが人的にも機械的にも高コストだと思うんだけどなあ。クエリキャッシュのヒット率も下がるわけだし、あんまりいいことはなさそうに思う。
どうしてもっていうならビューでやるかなあ。
62. PHP より賢い DB ロジック(queries, joins, views, procedures)を活用しよう。
データベース側のビューとかプロシージャとかの機能を活用しよう、ってことかな。
コメントとか制約とかから自動的に入力フォームを作るライブラリをつくったことがあるけど、JOINで壁にぶつかって途中で飽きて捨てたなあ。
63. SQL の省略表記を活用しよう。(例:INSERT INTO MYTABLE (FIELD1,FIELD2) VALUES ((“x”,”y”),(“p”,”q”));)
あんまりやると互換性で泣くのでほどほどに。個人的には、上の例だとprepareしてforeachかなあ。
なんか全部マニュアルに載ってることのような気がしてきた。PHPのマニュアルはすごく充実してるので暇なときに読んだりするといいと思う。
]]>09. スクリプト開始時間は time() でなく $_SERVER[‘REQUEST_TIME’] で取得。
13. if をたくさん使ってるなら switch を活用しよう。
17. $row[‘id’] は $row[id] より7倍速い。(速度じゃなく別の意味で)
31. コンパイルキャッシュを利用しよう。
32. memcached とか使おうよ。
35. 全部が全部 OOP でなくても良い。
38. メソッドを分割したくなったら後でいくらでもできる。
39. もとから用意されてる 関数たち を活用しよう。
40. 非常に時間のかかる関数 → C言語による拡張モジュール化も検討しよう。
41. コードをプロファイリングしてボトルネックを見つけよう。Xdebug とか。
47. ループ処理には foreach を使おう。
53. 一時的なファイルを作るときには tmpfile や tempnam を使おう。
今日から自動車保険見積もりサイトになります – うさだBlog / ls@usada’s Workshop
いま調べてみたけどあいかわらず自動車保険見積もりは$28.52と高値だった。そのままいろんなキーワードで調べてみた。
「料理」で調べると首位の「モンゴル料理($6.33)」が2位の「大豆料理($1.85)」に大差をつけてたりとか全体的によくわからない。あと「違法ドラッグ販売」に$0.05の値がついてたりしたので心当たりのある人は早く逃げたほうがいいと思う。
]]>本気でやるならonclick属性は避けてライブラリを活用すべき – id:HolyGrailとid:HoryGrailの区別がつかない日記
個人的には、onclick属性を書くのってCSSでいうと<span style="color:red;text-decoration:underline;">こんにちはこんばんは</span>みたいなもんだと思うので、長所短所もそれに準じる感じ。ただ、ちょっとしたハック(やっつけ仕事)としてやることはあるけど、本気と言うなら外に書くべきだよなあ、とは思う。
ライブラリは流行り廃りが激しいから、特定のライブラリに依存した「おまじない」ばかり覚えているのはどうかと思うなあ
やっぱり、 DOM を直接書けたほうが、知識としては幅広く使えると思いますよ。
それはそうなんだけど、最初からIE向けおまじないを意識しなきゃいけないのは茨の道だと思う。僕はOperaのUser JavaScriptから入ったのでIEが出てきたのは中盤になってからだけど、レベル1の状態でIEが出てきたら即死続出じゃないだろうか。
form=document.getElementsById("targetForm");
// 普通のブラウザ向け
form.addEventListener("submit",function(e){
alert("submitさせないっ");
e.preventDefault();
},false);
// IE向け
form.attachEvent("onsubmit",function(){
alert("submitさせないっ");
window.event.returnValue=false;
});
// クロスブラウザ手抜きver
(function(form){
var _tmp=form.onsubmit || function(){};
form.onsubmit=function(){
_tmp.apply(this,arguments);
alert("submitさせないっ");
return false;
}
})(form)
メソッドから引数からイベントの扱いから、alert以外すべて非互換なんてこんな書き分けは嫌がらせとしか思えない。手抜きverも、挙動を理解してないとたぶん厳しいし毎回こんなの書くのも嫌だ。
IEを無視するためにライブラリから入る、ってのは悪くないと思うなあ。運動のために階段を使う人が居てもいいけど、エレベータも捨てたもんじゃないですよ。
どっちみち何かつくるなら、まずやることは各ブラウザの違いを吸収するラッパー作りになるので、プロダクション環境では既存ライブラリか自作ラッパーかの違いでしかないと思うけど、それはまた別の話か。
なぜか誰も正面切って反対してなかったので書いておくけど、<a href="javascript:void(0);" onclick="somefunc();">あいうえお</a>とかやられると、このリンクは新規タブで開いていいのか普通に左クリックしなきゃいけないのか、と毎回判断しなきゃいけないのでやめてください。
<span class="clickable" onclick="somefunc();">あいうえお</span>とかCSSでそれっぽく見せるようにしてください。
RDocでマニュアルつくってみたけど、instance_evalで独自getter定義とか気持ち悪いことしてるのであんまり役に立たない気が……。
最初にログインしておかないと他の機能が動きません(raiseが発生します)。
mail="xxx@xxx.com" password="xxxxxxxx" Nicokit.login(mail,password)
いまんとこ自分のマイリスト一覧を取ってくるだけ。
「自分のマイリスト一覧」を正しい日本語でどういえばいいのかわからない。
my=Nicokit::My.new
# 自分のマイリスト一覧
my.list.each{|l|
puts l.title # マイリストのタイトル
puts l.description # マイリストの説明
}
l=Nicokit::VideoList.new("1626019")
# マイリストのタイトルと説明
puts "#{l.title} - #{l.description}"
l.videos.each{|v|
# マイリストに登録されてる動画一覧
puts "#{v.url} - #{v.title}"
}
v=Nicokit::Video("sm2637528")
# タグ
puts v.tags.join(", ")
# この動画を見た人は他にこんなのも見てます
v.recommend.each{|r|
puts "#{r.url} - #{r.title}"
}
# ローカルへ保存
v.flv.save("#{v.title}.flv")
タグとキーワードからそれぞれ検索できます。
s=Nicokit::Search.new("test") # デフォルトはキーワード検索
puts s.count # 「test」でキーワード検索したときのヒット数
s.switch(:tag) # タグ検索に切り替え
puts s.count # 「test」でタグ検索したときのヒット数
puts s.switch(:search).count # キーワード検索に切り替えてヒット数表示
# 「才能の無駄遣い」タグ、再生が多い順
s2=Nicokit::Search.new("才能の無駄使い",:tag,{:sort => :pv,:order => :desc)
# 動画を10件取得
# (デフォルト30。50とかページをまたぐような件数を指定してもよしなにやってくれるけど、そのぶんリスキー)
s2.video(10).each{|v|
puts v.id
}
# 該当する動画を全部取得 *危険
# (下記の「使用上の注意」参照のこと)
s2.video_all
必要な情報があればその都度nicovideo.jpへ接続して取ってきてるので、たとえば
my=Nicokit::My.new # ここではまだ何もしない
my.list{|l| # 接続(マイリスト一覧取得)
puts "#{l.title}" # 接続(リストタイトル取得)
l.videos.each{|v| # l.title取得時に取得済みなので接続しない
puts "#{v.title}(#{v.url})" # 接続(動画タイトル)
}
puts "#{l.title}おわり" # l.titleは取得済みなので接続しない
}
というようにコネクションを張りまくることになります。この例でいえば、l.videosが100個あるならv.titleのために100回アクセスします。
短時間に大量のアクセスをすると、ニコニコ動画に「やめてくださいエラー」を出されますのでご注意を。Nicokit側では一切ウェイトを入れてないのでeachに適宜sleepを仕込むなどしてください。今のところキャッシュなども未実装です。
データ的にはl.videosやl.titleの取得時に動画のタイトルも既知になるわけですが、現在のところそういうエコロジーな機構は用意されていません。
RDocとgitとその他いろいろが初物なので出来が微妙かと思いますが、リポジトリはオープンになっているので誰か添削してくれるとうれしいです。
]]>0.000000092秒が0.00000092秒になったところで心底どうでもいい。バッチ処理はPHPに向いてない と思うし、最適化するにしてもそんなミクロなとこより直すべき点が他にある。Webならファイルをgzipする、キャッシュを正しく使う、APC入れる、Apacheの不要モジュールを外すなどなど。
@を全部issetに直したら許容できるパフォーマンスが出た! なんてことは未来永劫ありえない。100万回もやれば結構な差がつくだろうと思ってたのに思いのほか微差だったので900万回ループ追加したらようやく1秒単位の違いになった、ていうシチュエーションがすべてを物語ってる。
速度はどうでもいい。問題はE_NOTICEの理想と現実をどう妥協していくかだと思う。
そもそもerror_reportingのデフォルト値を変えてE_NOTICEを加えてる時点で@なんか使わない宣言をしているに等しいと思うので、if(@$a["foo"])とか書くのはやっぱりおかしい。register_globalsをオフにしておいてextract($_REQUEST)
するような矛盾を感じる。
そうはいってもif(isset($_GET["foo"]) && $_GET["foo"] === "bar")なんてアホらしくていちいち書いてられないってのもわかる(最初のissetがないとE_NOTICE発生)。それに毎度毎度issetを書くなんていったらPerlとかRubyとかPythonとかJavaScriptとかLuaとかLispとかSmalltalkとか(中略)あたりの人に勤勉とかいって笑われる。
現状これに対するメジャーな妥協案は、
function _g($ary,$key,$default=null){
return isset($ary[$key]) ? $ary[$key] : $default;
}
if(_g($_GET,"foo","") === "bar"){
// do something
}
$display=_g($options,"display","none");
みたいなことをしてデフォルト値処理を自前で実装するやり方だろうか。なんかなあ。
と思ってちょっとググってみたところ、array_mergeでデフォルト値決める のがスマートだなあと感心しました。ただ、深い配列の場合は期待と違うくっつきかたになるのでやっぱり注意。
開発時にE_NOTICEを有効にすることにはいくつ かの利点があります とか書いてるけど、E_NOTICEしててよかったことが1年以上のあいだで一度あったかどうか。逆にE_NOTICEエラーのせいでheader()が機能しないなど邪魔されたことは数知れず。
このへんのバランスは変数名typoのしやすさで個人差がありそうだけど、僕の場合は割に合わないのでとっととやめます。さようならE_NOTICE。ありがとうジョジョとIPA。こんにちは世界。
]]>大きいファイルを扱うならfile()なんか使っちゃいけません。速い遅い以前の問題です。
本文終わり。以下蛇足。
------------------------------ 30,000 lines ------------------------------ # -- time ./php_file.php 30k.txt 24640 0.38user 0.07system 0:00.49elapsed 93%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+5505minor)pagefaults 0swaps # -- time ./php_exec.php 30k.txt 24640 0.51user 0.04system 0:00.60elapsed 93%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+2709minor)pagefaults 0swaps # -- time ./php_file2.php 30k.txt 24640 0.32user 0.06system 0:00.39elapsed 97%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+1691minor)pagefaults 0swaps # -- time ./my_rb.rb 30k.txt 24640 0.11user 0.00system 0:00.11elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+764minor)pagefaults 0swaps # -- time ./my.sh 30k.txt 24640 0.22user 0.00system 0:00.23elapsed 96%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+717minor)pagefaults 0swaps
↑3万行程度ならまだ比較対象になりうるけど、
------------------------------
300,000 lines
------------------------------
# -- time ./php_file.php 300k.txt
Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 38623201 bytes) in /tmp/php_file.php on line 3
Call Stack:
0.0002 57944 1. {main}() /tmp/php_file.php:0
0.0002 57944 2. file() /tmp/php_file.php:3
Command exited with non-zero status 255
0.02user 0.00system 0:00.02elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+1660minor)pagefaults 0swaps
# -- time ./php_exec.php 300k.txt
246400
2.13user 0.15system 0:02.46elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+2708minor)pagefaults 0swaps
# -- time ./php_file2.php 300k.txt
246400
1.88user 0.69system 0:02.63elapsed 98%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+1692minor)pagefaults 0swaps
# -- time ./my_rb.rb 300k.txt
246400
1.00user 0.00system 0:01.05elapsed 96%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+794minor)pagefaults 0swaps
# -- time ./my.sh 300k.txt
246400
2.10user 0.15system 0:02.45elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+717minor)pagefaults 0swaps
↑30万行になるとレース脱落。
------------------------------
3,000,000 lines
------------------------------
# -- time ./php_file.php 3m.txt
Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 347608801 bytes) in /home/clover/tmp/php_file.php on line 3
Call Stack:
0.0003 57940 1. {main}() /tmp/php_file.php:0
0.0004 57940 2. file() /tmp/php_file.php:3
Command exited with non-zero status 255
0.02user 0.01system 0:00.04elapsed 89%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+1663minor)pagefaults 0swaps
# -- time ./php_exec.php 3m.txt
2217600
19.76user 1.35system 0:22.80elapsed 92%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+2712minor)pagefaults 0swaps
# -- time ./php_file2.php 3m.txt
2217600
15.74user 5.97system 0:24.57elapsed 88%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+1689minor)pagefaults 0swaps
# -- time ./my_rb.rb 3m.txt
2217600
8.97user 0.30system 0:09.41elapsed 98%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+893minor)pagefaults 0swaps
# -- time ./my.sh 3m.txt
2217600
19.25user 1.11system 0:20.76elapsed 98%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+718minor)pagefaults 0swaps
↑300万でももちろん死ぬ。
これはfile()がファイルの中身を全部メモリに置くからそうなる。30万行、37MBものデータをPHPで一度に持とうとした瞬間にメモリオーバー。要するにfile()はスケールしない。
他のやりかただとその都度必要な分だけメモリに読み込むので、ログが何億行あろうとメモリがあふれることはない(タイムアウトの可能性とかはある)。
元のやつとは時間計測のしかたが違う。元のはPHPスクリプト内でのおはようからおやすみまで、上でやってるのはプロセスのおはようからおやすみまで。
#!/usr/bin/env php
<?php
$file = file($argv[1]); // ここで死ぬ予定
$counter = 0;
foreach ($file as $data) {
if (preg_match('/02:[0-5][0-9]:[0-5][0-9]/', $data)) {
$counter++;
}
}
echo $counter."\n";
#!/usr/bin/env php <?php $cmd = 'grep 02:[0-5][0-9]:[0-5][0-9] '.$argv[1].' | wc -l'; exec($cmd, $cnt); echo $cnt[0]."\n";
#!/usr/bin/env php
<?php
$counter = 0;
$fp=fopen($argv[1],"r");
while($line=fgets($fp)){
if (preg_match('/02:[0-5][0-9]:[0-5][0-9]/', $line)) {
$counter++;
}
}
fclose($fp);
echo $counter."\n";
#!/usr/bin/env ruby
cnt=0
File.open(ARGV.shift){|fp|
cnt += 1 if fp.gets.match(/02:[0-5][0-9]:[0-5][0-9]/) while !fp.eof?
}
puts cnt
#!/bin/sh grep "02:[0-5][0-9]:[0-5][0-9]" $1 | wc -l
*php_file脱落
300万行だとRubyがぶっちぎりトップで、それ以外は横並び。3万と30万はばらついてるけど誤差かな。
ITProの記事を見る限り、php_execはphp_file2以上シェルスクリプト以下、って感じだと思ってたんだけどそうでもなかった。そもそも3万行のところでphp_fileに負けてるのが解せない。PHP CLIだからとか計測方法が違うからとか心当たりはあるけどどれもピンと来ないなあ。
Rubyがシェルスクリプトより速いのは意外だったけど、たぶんPerlとかPythonとかのほうが速い気がする。
例えば、PHPを避ける。
PHPはApacheとイチャイチャしてなんぼだと思います。バッチ処理や解析は荷が重い。
]]>sudo gem install xmpp4rが必要。
require 'rubygems'
require 'xmpp4r/client'
class BotDaemon
def initialize(user,pass,botname)
@botname=botname
@client=Jabber::Client::new(Jabber::JID::new(user+"/"+@botname))
puts "connect: #{@client.connect}"
@client.auth(pass)
@client.send(Jabber::Presence.new.set_show(:chat))
end
def parse(msg)
tmp=msg.body.match(/^Direct from (.*):\n(.*)/).to_a
if tmp.length == 0
tmp = msg.body.match(/^(.*?): (@.*) (.*)/).to_a
result={
:user => tmp[1],
:message => tmp[3],
:is_direct => false,
:prefix => "@"+tmp[1]+" ",
}
else
result={
:user => tmp[1],
:message => tmp[2],
:is_direct => true,
:prefix => "d "+tmp[1]+" ",
}
end
result[:botname]=@botname
result
end
def daemon(&block)
mainthread = Thread.current
@client.add_message_callback{|msg|
next if !msg.body
info=parse(msg)
send=block.call(msg,info)
p send
m2=Jabber::Message::new(msg.from,send)
m2.type=msg.type
@client.send(m2)
}
Thread.stop;
@client.close;
puts("Done.");
end
end
こんな感じで使う。
#hellobot.rb
require "botdaemon"
d=BotDaemon.new("foobar@jabber.jp","password","[twitterbotname]")
d.daemon{|msg,info|
"#{info[:prefix]"}はろー"
}
ruby hellobot.rbするとずっと待ってる。Ctrl+Cとかで終わる。
ほっとくとJabberかなんかをタイムアウトしたりするらしいのでそのうちちゃんとかく。とりあえず@かd投げたら所定の位置の天気予報かえすやつつくって終わった。
]]>foreach一択。
PHPに配列と連想配列の区別がない以上、イテレーションの現場にforの居場所はないと思われる。添字に数字以外が含まれるかもしれない配列をcount()と$i++で回すのはなんかリスキーだ。
ていうかメリットとして挙げられている「ループ内でデータが直感的に参照できる」てのはむしろ冗長にしか見えない。for()の中に初期化、続行/中断条件の定義、毎回実行する構文、と3つも決まりきったことを書かなきゃいけないのも面倒。
PHPにはブロックスコープとかの概念もないので、用済みのforeach用変数がループ終わっても参照できるとかどうしようもなく不細工な面もありますが、まあそれはそれで。あとforeach((array)$var as $v){echo $v;}とかするとWarningが出なくなってだいたいの場面では便利です。
目的別に分けるとだいたい以下の3パターンだと思う。
「配列の各要素と添字がペアで欲しい」は特殊な用途だと思うのでとりあえず無視。たぶん、たまたま添字が数字だけで出来てる連想配列じゃないかと思うので、2.と同じ方法で対処できるかもしれない。
「連想配列の添字だけ欲しい」は、連想配列の添字だけを配列にして(PHPだとarray_keys()、RubyでいうHash#keys)しまえば、1.と同じってことになる。
「連想配列の各要素だけが欲しい」もまずないと思われるので無視。あったとしても添字を捨てるか、array_values()相当して配列作れば同じこと。
「配列の決まった数だけ欲しい」はそもそもイテレーションじゃない。それをいうと3.もそうじゃないか、って疑念はわきますけど、まあ、その、特例ってことで許してください。なんとなくarray_slice()とかarray_filter()して配列を部分的に抜き取るのが王道じゃないかなあ。
というわけで、以下に書いた以外にもやりかたはあると思いますが、もっとも素朴な実装例ってことでひとつ。
eachメソッドを呼べばいい。
決まった回数の反復には[0..n]な配列を作ってeachしてもいいけど、通常はNumberクラスのメソッドを使う。
arr=[1,2,3]
# 配列
arr.each{|v|
puts v
}
# 連想配列
hash={1=>"a",2=>"b"}
hash.each{|k,v|
puts "#{k} => #{v}"
}
# 特定回数
0.upto(9){|n|
puts n
}
Array/Hashクラスのeachメソッドを呼ぶ、というスタイルなのでPHPのforeachとは主客逆転してる。
僕が知ってる範囲ではいちばんシンプルな解法だと思う。
Pythonは配列(list)と連想配列(dict)で若干構文が変わるのでなんか嫌だった。
# 配列
arr=[1,2,3]
for v in arr:
print v
# 連想配列
hash={1:"a",2:"b"}
for k,v in hash.iteritems():
print "%s => %s" % (k,v)
# 特定回数
for n in range(10):
print n
iteritems()がどうしても気持ち悪い。
名前こそ全部forだけど、特性はPHPのforeachに近い。特定回数の実行には[0..n]な配列を作ってforするみたい。
Pythonは3日くらいしかまともに触ってないのでまずいやりかたなのかもしれない。
問題児。
PHPは配列と連想配列を区別しないけど、JavaScriptは連想配列とObjectオブジェクトを区別しない。連想配列はキーと値のペアではなく、オブジェクトのプロパティとその値という扱い。
// 配列
var arr=[1,2,3];
for(var i=0,len=arr.length; i<len; i++){
alert(arr[i]);
}
// 連想配列
var hash={1:"a",2:"b"};
for(var p in hash){
if(hash.hasOwnProperty(p)){
alert([p," => ",hash[p]].join(""));
}
}
// 特定回数
for(var n=0; n<10; n++){
alert(n);
}
hashはObjectオブジェクトなのでhasOwnPropertyでチェックしないと変なメソッドや不要なプロパティまで引っかかるかもしれない。あと配列走査に原始的なforしか手段がないのでイライラ。
もちろんArray.prototype.eachを定義してイテレーションする手もある。XPathResultとかNodeListとかも同様。しかしながら、連想配列=Objectであるかぎり、Object.prototype.eachにはちょっと抵抗がある(連想配列以外も含めたあらゆるオブジェクトが実質的にeachメソッドを持つことになる)。
Prototype.jsはそのためにHashクラスをつくって、連想配列とObjectを分離しようとした。Hash#toQueryStringとか、通常のJavaScriptでは心理的になかなかなしえない拡張をしまくってる。
jQueryは巨大なDSLというか、JavaScript自体を再定義してるような感じ。jQueryを使うならJavaScriptの流儀をだいぶ忘れてjQueryの流儀に染まる必要がある。
他はあんまり詳しくないけど、だいたいこういう事情があるので生のJavaScriptを触りたい人はライブラリの使用をためらう傾向にあるんだと思います。
なんか自分用メモになってしまった。他言語での例に照らせばforeachのほうが自然だって言いたかったはずなのに。あれー。
]]>「分かりやすい表現」の技術―意図を正しく伝えるための16のルール
実家でぼーっとしてたら床に転がってるのを見つけたので拾って読んだ。JR東京駅の案内板がいかに貧弱かという実例を挙げつつ改善例を示して、徐々に深いところへと切り込んでいく感じ。
要は情報デザインの話。
僕は社内用の資料をHTMLでつくる癖があって、文書を共有しやすいって以外にも、論理構造を意識しながらh2とかulを使うので自然と情報が整理されてくるって副作用がある。さすが論文用につくられたマークアップ言語なだけある。
上の本のなかでダメな資料の例としてセミナーの日程表が出てた。元は紙の資料だと思うけど、これの悪い例といい例はそれぞれHTMLでいうとこれくらい違う。
<p> 6月3日プログラム<br /> I. 国際的関税制度<br /> 1. WTO諸協定の内容<br /> 2. 関税関連法令の概要<br /> II. 輸出通関手続きの基本<br /> 1. 輸出通関のチェックポイント<br /> 2. 再輸出と戻し税<br /> 6月4日プログラム<br /> III. 輸入通関制度<br /> 1. 商品分類<br /> 2. 関税率<br /> 3. 特恵関税制度<br /> IV. 減免税制度<br /> 1. 減免税制度概要<br /> 2. 再輸入減免税制度 </p>
<h2>6月3日プログラム</h2>
<h3>I. 国際的関税制度</h3>
<ol>
<li>WTO諸協定の内容</li>
<li>関税関連法令の概要</li>
</ol>
<h3>II. 輸出通関手続きの基本</h3>
<ol>
<li> 輸出通関のチェックポイント</li>
<li> 再輸出と戻し税</li>
</ol>
<h2>6月4日プログラム</h2>
<h3>III. 輸入通関制度</h3>
<ol>
<li>商品分類</li>
<li>関税率</li>
<li>特恵関税制度</li>
</ol>
<h3>IV. 減免税制度</h3>
<ol>
<li>減免税制度概要</li>
<li>再輸入減免税制度</li>
</ol>
上の例がただベタに羅列してあるだけなのに対し、下の例は構造化されている。見出しと列挙項目が区別され、何が何に属するのか、(ブラウザで見れば)視覚的にも分かりやすい。整理されてるので頭にも馴染みやすい、つまり分かりやすい。
別にHTMLを使わなくともグループごとにわけるって意識があれば、同じグループを同じ色で塗ったり、矩形や丸で囲んだり、セクションとセクションのあいだに線を引いて区別したりできる。メールとか図を使えないただのテキストでも、改行や空行で段落を切ったり、見出しの頭に記号(*とか#とか)をつけて目立たせるなど、できることはある。
そういう本。どうでもいいけど東京の分かりづらさは半端ない。悪名高い「つぎ」「こんど」「そのつぎ」 って表示とか、JRは明らかにやる気がないうえに駅員もやる気がないので狂った街だと思う。暴動を起こせばいいと思う。
]]>あんまりいうとメガネっ娘に撲殺されるのであれだけど、目に関してはヘビーユーザーだと思う。
幼稚園から小中高大学と、ファミコン〜アーケードで格ゲー、PC、夜中に読書など目に悪いとされることを恒常的にひととおりやりまくっているわりに視力はまだ1.5(左1.2、右1.5)だ。会社に入ってから左が1.5→1.2に悪化したものの、日常生活にさほどの支障はない。昔から1日数時間はテレビなりモニタなりを見続けている。
今日も8時間ほど休みなしでずっとプログラム書いてたけど、目はまったく疲れてなくて脳と体だけが疲れた。ちなみにVimのカラースキームはZenburn を愛用しています。www.vim.orgにあるやつはバージョンが古くて補完の色が変わらないので注意。
というか目が疲れたことがない。肩がこったりこめかみが痛くなることはあるけど、目が疲れるという状況はほとんどない。目が乾いたりしょぼしょぼしたりといったことはあるものの、眼精疲労なんて大仰なものではない。
眼の健康のために、部屋を暗くしてPC作業をしましょう。明るくではありません。
照明よりモニタの輝度を下げたほうがいいと思う。モニタはデフォルト設定で使うものじゃない。
モニタにもよるのでなんともいえないけど、輝度は10〜40くらいで充分。デフォルトだと100とか80とかまぶしすぎて見てられない。特に液晶モニタはCRTより発光が強いと感じる。まぶしさを発色のよさだと錯覚させる陰謀なんだと思う。
真っ白の画面(ブラウザ立ち上げてabout:blankを開いて最大化)を集中せずにぼんやりと見て、まぶしいと感じないくらい、薄いほこりがかぶってるような純白じゃない白くらいでちょうどいい。純白である必要がそもそもない。白とライトグレーを判別できれば充分。
コントラストはまぶしさを感じない範囲でできるだけ高いほうがいい。コントラストが低いとモニタに近づきすぎたり、凝視してしまったりして逆効果なのでそこらへんは要調整。デルの安いモニタは22インチワイドと面積が大きいわりに、輝度を下げると色がおかしくなって使い物にならなかったので封印してる。いま使ってるのはMITSUBISHI RDT1714VMだけど、輝度30コントラスト45で使ってる。
スキー用のサングラスをしてしまいます。掛けた状態と外した状態を見比べると、まぶしくないので、その効果が非常に分かりやすい。
というのも、要するにモニタがまぶしすぎるから遮光してるだけだと思うので、まずモニタ設定いじったほうが手っ取り早いように思う。
ていうか、僕の場合はたぶん普段からものをちゃんと見てないからだと思う。焦点があってないというか、あまりものを見ようとしていない。網膜には写ってるけど意識はそれを見てないというか。
意識して見るとピントを合わせようとして細かいシークが起きて視神経だか筋肉だかが動きまくって、結果疲れることになるんでは。エヴァ劇場版を見に行ったとき、小ネタを見落とさないよう集中して見てたらさすがに疲れたので、たぶんあれがデフォルトの人は環境に関係なく疲れる気がする。
というわけで、見るともなく全体を見る が最強のライフハックだということで締め。
]]>普通に主人公が街を歩いてる映像にレイヤーを重ねて声を入れ、演者がそれに合わせて演技(のけぞる、倒れるなど)するって手法が目立ちました。ちっこい女の子がワイヤーアクション+高速再生で敵を瞬時に蹴り飛ばしたり、変身シーンで別次元へ転送されることなくその場でエフェクトとともに変身したり、というのも目新しかったです。継ぎ目が見えない字義通りシームレスなエフェクトで見ててたのしい。
銃の発砲も、従来の特撮では着弾点から火花が出る程度でしたが、電王の場合は銃口からボウリング玉ほどの青い球体CGがごく自然に発射されてました。画面がスローモーションになって、撃たれた敵が倒れながらそれを避けて球体が肩をかすめたあと同様の球体を発射して反撃する、っていうシーンはマトリクスっぽかったです。
普段は映画もアニメもテレビもあまり見ないのでどうかはわかりませんが、AR的な エフェクトが流行りなんですかね。平日夜中のドラマで木村拓哉あたりが映像的に魔改造されたり、昼ドラで姑が非人間的な体術を駆使して嫁に嫌がらせするのはもう普通なんでしょうか。個人的にはメカ水戸黄門に期待しています。
あといっしょに行った人いわく内容的には微妙だったらしいですが、僕はほぼ映像技術しか見てなかったのでよくわかりません。放映終了した電王より現行ライダーであるキバのほうが強いというアピールや、全変身パターンの網羅+おまけといった、大人の事情は垣間見れました。
]]>GoogleではC++、Java、Python、JavaScriptというフェンスの中で遊ぶ必要がある らしいので、次はJavaだと思う。PHPはまずないだろうな。ていうかRhino on Railsの故事からすると、サポートされないならPythonでインタプリタ書けばいいじゃない。
jrubyを使えばRubyのサポートも可能なはずだとか、GWTをフロントエンドにしてバックエンドでもJavaが動いてるのってどうよとか、JavaScriptをサポートするとどうなるだろうとか、出来が微妙なphpinfo()のパロディをGAEに上げたりとか 、C#を大絶賛してみたりとか 、わりとみんな好き勝手なこと言ってて面白かった。
あとだいたい/^(please)?add(for)?(.*?)(support)?/xって感じの正規表現にマッチするタイトルなのに、PHPだけ「PHP support is a must」とか言ってて調子のるなと思った。こんなに愛のないPHPコード
を書くGoogleがPHPなんかサポートするわけない。
他にはSSLサポートしてくれとか、ファイルアップロードしたいとか、cron的なものが欲しいとかがあった。日本語関係の要望はとくになし。
]]>報告してきた人はこれを機会に乗り換えを、とかほのめかしていたのでglucose勧めといた。ついでにフィードリーダーについてまとめてみる。
RSSOwl →glucose →FEEDBRINGER(サービス停止)→ Fresh Reader →livedoor Reader(LDR) と使ってきました。だんだんウェブベースのものへとシフトしていってる感じですね。iGoogleもいちおう使ってますがメインではありません。Fastladderは中身がほぼLDRなので省略してます。評判の高いbloglines はなぜか使ったことがないです。
時系列じゃなく利用期間の長さで並び替えると、livedoor Reader→glucose→FEEDBRINGER→RSSOwl→Fresh Reader。LDRが2年近く、Fresh Readerで2ヶ月くらいかな。
他に、触ってはみたもののすぐ辞めたのがFirefox(Sage)、Google Reader、Opera Mail、はてなRSS、パラボナミニ、cococあたりです。Googleは手動で既読化しないといけないのが面倒だったのとその他ですぐにLDRへ出戻り、はてなRSSはなんか微妙、その他は数を読むのに適していないという理由でそれぞれ辞めました。
とりあえず今のメインはLDR。他のに乗り換える気は今のところないです。
glucoseやgooRSSリーダーなんかがここです。Becky!プラグインやOutlookプラグインなんかもここに入ります。
メールのように自分で巡回してフィードを集め、メールのようにフィードを読むものです。たぶんいちばん人気のあるタイプです。
メールチェックのお供にどうぞ。
cococやパラボナミニあたりが該当します。
タスクトレイに常駐し、更新があればポップアップします。
10〜20くらいの、それほど更新が頻繁でないフィードを購読してる人向けかと。
Firefox、Opera、IE7、Sleipnirプラグインなどです。Safariもそうだったかな。
ブラウザの機能として組み込まれています。
デスクトップ型メーラー系で、いちいちブラウザ起動して本文読みに行くのが面倒な人かなあ。
Google Reader、livedoor Reader、bloglines、MODIPHIなど。
フィードを読むための専用ウェブサービスです。
どちらかというとヘビーユーザーが愛用してるイメージですが、Yahoo!なんとかリーダーはそうでもないはずなので案外幅広いのかも。
取りこぼしをなくしたい人とか、いちいち手動で巡回するより1ヶ所で終わらせたい人向けかなあ。
サービスによって特色があるのでそれ以上はなんともいえない。
Fresh Readerとオープンソース版Fastladder。
自分が管理するサーバにインストールして使う。環境にもよるけどレンタルサーバやローカルのApacheでもたぶんOK。
ウェブサービス系が好きだけど、何かと不安や不満がある人向け、かなあ。
ヘビーユーザー御用達。LDRは文学。Fastladderは人生。そんな感じの大絶賛。
キーボードショートカットを使えば1000フィード購読しててもきちんと読める(と謳ってるし、実際数千フィード購読してる人もちらほら居る)。
Greasemonkeyによるちょっとしたカスタマイズなども豊富。
軽くてシンプル。デスクトップ型メーラー系のリーダーとして実に真っ当。
長いこと使ってないから新機能については詳しくないけど、フィードリーダー古参ながら現在の開発も活発なので、全然ダメってことはまずないはず。万人におすすめできる。
ダラ見用として最高峰。
iGoogleを開くのは「なんか面白いものはないかな」とテレビをつけるようなものなので、そこまで関心はないけどたまに面白いものを登録しておくと便利。はてなブックマークの注目エントリなんかを登録してある。
まったくもって微妙なデータですがいちおう。数が少ないのはご愛嬌ってことで。
たぶん、ってついてるのは購読者数を確認できなかったものです(あるいはやろうと思えばできるけど面倒)。あとブラウザ統合型はその性質上、RSSを取りにきたのか普通に開いただけなのかが判別できないので数えてません。
]]>
# sudo apt-get install php5-dev php-pear postgresql libpq-dev
# sudo pecl install pdo pdo_pgsql
(php.iniとかにextension=を追記)
# cat segv.php
<?php
echo "no print";
$pdo=new PDO($dsn,$user,$pass);
$stmt=$pdo->prepare("INSERT INTO table (a,b) VALUES (:a,:b)");
$stmt->execute(array("a"=>"im a","b"=>"im b"));
なぜかこの動作じゃ再現しなかったけど、こんな感じの状況(INSERT文をprepareしてexecuteで配列渡す)で、エラーメッセージもechoもvar_dumpも出力されず完全に無言。apacheのerror.log見るとsegfaultしてた。
[Sun Apr 13 17:20:36 2008] [notice] child pid 5930 exit signal Segmentation fault (11)
どこかの時点で真っ白になってしまうので、1行ずつecho __LINE__;exit;を追加していくと、prepareのところが原因と判明したけどどうしようもない。ググっても解決法は見つからず。仕方ないのでPDO捨ててMDB2使うことにした。
そしたらなんか全体的に軽い。というかスムーズ。特に何かしたわけでもないのにとても快適。そして何よりもOperaが軽すぎる。タブ開くのに0.3秒 とか書いたけど、今は0.04秒くらいと桁違いに速くなった。どういうことだ。
思い当たるのはgtk-qt-engine入れてないくらいだけど、Operaに限らず全体的に速いので関係ないかもしれない。
これでブラウジング環境のためにWindowsに戻るべきか悩まなくて済むようになったものの、原因がわからないので再現しようがなくUbuntuにバインドされたともいえる。まあいいか。
いや、どうも原因わかった気がする。スキンをBreeze Simplified MICRO にしたら劇的に遅くなった。他のBreezeシリーズも重い。デフォルトやDimple のときに比べてかなり遅い。体感できるくらい違う。ていうかDimpleがだいぶ軽い。
くそうFirefoxのときはわりとスキンの重さも考えてたのに、Operaはうっかりしてたなあ。そうかスキンが原因か。Breeze SImplified MICROはDImpleの半分くらいのタブ幅なので重宝してたのになあ。
]]>padding top=5 padding bottom=2
を、
padding top=0 padding bottom=0
にしただけ。
あと色が青くて落ち着かないので、カラースキームはシステムカラーにしてる。
| Dimpleデフォルト | Dimple変更版 |
|---|---|
![]() |
![]() |
上から見ていくと作業履歴っぽいですね。
]]>諦めて床に転がってたKubuntuのCDで再インストールした。とりあえず旧環境を残しておいてデュアルブート出来るようにした。
もうすぐ次期安定版Ubuntuが出るとかいうタイミングで7.10を入れるタイミングの悪さ、KubuntuなのにGNOME入れて使ってる意味のなさ、などなど突っ込みどころも多く取り揃えております。あーあ。
]]>Amazon ECSのXMLをparseしようとしてエラーに遭遇した。
UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 0-1: ordinal not in range(128)
Amazonが返すXMLには当然日本語が入ってる。それをどういうわけかasciiと判断して処理しようとしてエラーになってるらしい。
コードの先頭行でencode: utf-8は指定してる。import sys; sys.setdefaultencodingは存在しない。xml.dom.minidom.parseStringに文字エンコーディングを指定する方法はない。
ていうか<?xml version="1.0" encoding="UTF-8"?>くらい読め。お前はIEか。
xml.dom.minidom.parseString(xmlstr) # error
xml.dom.minidom.parseString(xmlstr.decode("utf-8")) # error
xml.dom.minidom.parseString(xmlstr.decode("utf-8","utf-8")) # error
uxmlstr=unicode(xmlstr) #error
uxmlstr=unicode(xmlstr,"utf-8") #error
print "あ".encode("utf-8") #error
print "あ".encode("utf-8","utf-8") #error
print "あ".decode("utf-8") #error
print "あ".decode("utf-8","utf-8") #error
print unicode("あ") # error
print unicode("あ","utf-8") # error
print u"あ" # error
print "あ" # ok
どーせーっちゅうねん。がーーーー
]]>
LDRのアカウントが会社用と自宅用で2つあって、LDR上でiするとそれぞれ会社用、自宅用のlivedoor clipに分散してめんどいのでGoogle App Engine使って管理しようかと思い立った。
とりあえずスクリーンショットの状態まで持ってくるのに2時間近く掛かって疲れ切ったのでまた明日。Python初挑戦のわりにここまで出来たらとりあえず満足だ。
<html>が2つあったりprint dir(なんか)がそのままだったりと実用には耐えないガラクタだけど、いちおう動作はしてる。
application: mybookmark version: 1 runtime: python api_version: 1 handlers: - url: /.* script: main.py
from google.appengine.ext import db
from google.appengine.api import users
class Bookmark(db.Model):
title=db.StringProperty(required=True)
url=db.StringProperty(required=True)
comment=db.StringProperty()
append_date=db.DateTimeProperty(auto_now_add=True)
is_open=db.BooleanProperty(default=True)
owner=db.UserProperty()
from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
import wsgiref.handlers
import os
from google.appengine.ext.webapp import template
from bookmark import *
def bookmark_list(bookmarks):
path=os.path.join(os.path.dirname(__file__),"list.phtml")
return template.render(path,{"bookmarks": bookmarks})
class MainPage(webapp.RequestHandler):
def get(self):
self.response.out.write(dir(template))
path=os.path.join(os.path.dirname(__file__),"form.phtml")
self.response.out.write(template.render(path,{}))
pets=db.GqlQuery("SELECT * FROM Bookmark ORDER BY append_date DESC");
self.response.out.write(bookmark_list(pets))
class UserForm(webapp.RequestHandler):
def post(self):
user=users.get_current_user()
if not user:
self.redirect(users.create_login_url(self.request.uri))
else:
#print dir(user)
self.response.out.write(self.request.get("title"))
tmp={}
#for key in ["title","url"]:
# tmp[key]=self.request.get(key)
Bookmark(
title=self.request.get("title")
,url=self.request.get("url")
,owner=user
).put()
self.redirect("/")
app=webapp.WSGIApplication(
[
("/",MainPage)
,("/post",UserForm)
],debug=True
)
wsgiref.handlers.CGIHandler().run(app)
<html>
<body>
<form action="/post" method="post">
<table>
<tr>
<th>title</th>
<td><input type="text" name="title" /></td>
</tr>
<tr>
<th>URL</th>
<td>
<input type="text" name="url" />
</td>
</tr>
</table>
<p>
<input type="submit" />
</p>
</form>
</body>
</html>
<html>
<body>
<ul>
{% for b in bookmarks %}
<li>
<a href="{{ b.url}}">{{ b.title }}</a>({{ b.append_date.isoformat}})
</li>
{% endfor %}
</ul>
</body>
</html>
]]>import。fooモジュールだかクラスだかをfoo.pyってファイルに記述して、import fooするらしい。
名前空間を使うなんかよくわからないやり方はfrom org.tt25.foobar import baz。この場合bazモジュールだかクラスだかが読み込まれる。
感覚的にはimportが先だと思うんだけどなあ。from foo.bar.bazって書いてるうちに何をimportしようとしたか忘れる。
Pythonでの名前はlist/dictっぽい。たぶんだけど。
定義のしかたはそれぞれ
l=[1,2,3]
d={"key": "val","key2": "val2"}
っぽい。JavaScriptと同じか。
arr=[1,2,3]
arr.join(",") # error
",".join(arr) # ok
なんでArray#joinじゃなくString#joinなのかはFAQらしいのでググれ。
for(l in [1,2,3]):
print l
ついでにハッシュ(辞書?)。
dict={"a": 1,"b": 2}
for(k,v in dict.iteritems():
print k,v
dir(obj)
str=”こんにちは%sの%sさん” % (where,user)
あるいは、
str="こんにちは%(where)sの%(user)sさん。" % {"user" : user,"where" : where}
from string import Template、でいいのかどうかよくわからん。
/A/B/foo.pyってファイルレイアウトがそのままfrom A.B import fooみたいにネームスペースになるらしい。詳細不明。Google App Engine SDKのディレクトリが__init__.pyだらけだったけど、こいつの正体も不明。
ざっと気になったのはこのへん。
逆に良い点。
などなど。特にタブ幅指定はいいなあ。
で、本日のメインイベントUser JavaScriptないしGreasemonkeyのエミュレートなわけですが、風博士はRubyで拡張できるらしいのでそれを使ってやろうと思ってた。結論からいうと無理だった。
ドキュメントがない。「風博士 ruby」で検索したときのトップが2006年1月のMLへの投稿 で、状況は「kazehakase ruby」とか英語圏を視野に入れてもさほど変わらず。
仕方ないのでMLにあるact_helloを導入してみて、p actionとかp kzとかp kz.methodsしてみて何が出来るのか把握しようとする。
3つある引数のうち、最初の2つはGTKオブジェクトなのでたぶん関係ないと判断し3つめのkzを集中的に調べてみる。とりあえずクラス名がkz::windowらしいのでそれでググる。CVSリポジトリばっかり出てくるけどドキュメントらしきものは皆無。仕方ないのでソースをあたる。Cなんて読めん。
ext/ruby の中にRubyとCの架け橋らしきものを発見したので、とりあえずkz-rb-window.cを開いてみる。15 months agoとか書いてるけど気にしない。
Userscripts.org のフッタに書いてある「Because it’s your web.」なら、風博士のRuby拡張は「Because it’s your webbrowser.」みたいな違い。あるいはSleipnirプラグインなんかも風博士に近いのかも。
いちおうkz.load_url('javascript:var s=document.createElement("script");s.src="foo.js";document.body.appendChild(s);void(0);')とかしたら似たようなことは出来たけど、何かしらの操作(マウスジェスチャやキー押下)が必要な点でしょせん回りくどいbookmarkletでしかない。ページ読んだら自動で実行してほしい。
ext/ruby/にあったCを全部読んだけど、求めるものはここにないってことで諦めた。
これも失敗。
WEBrickでプロキシサーバを作って遊ぶ とかを参考にいろいろやってみたけど、動作が安定しない。slashdotでは動くのにGoogleでは動かないなど。文字コードとかの影響かもしれないけどslashdotもGoogleもUTF-8だしなんやかやで投了。
妥協案。
ローカルのloader.jsをbookmarkletで挿入して、loader.jsで何をするか制御する。file:///で読み込もうとするとabout:configからsecurity.checkloaduriをfalseにしてもセキュリティおばさんが怒って読んでくれないので、ローカルで稼働してるApacheにVirtualHost振ってHTTP経由で読むようにした。
FirefoxはFirefox3 beta 5入れたらたぶんプロファイルごとぶっ壊れたっぽくて放置中。3の正式版待ちかなあ。
Operaは冒頭のようにタブ開閉が遅い。タブ開閉が重いのはGNOMEでもXfceでもfluxboxでも大差ないので、QtかOperaの問題っぽい。タブ開閉以外はそれほど気になる重さでもない。
風博士は軽さの点で現時点最強だけど、あれとかこれとかができない。
次はVMWare上のWindows Operaを試そうかと7割くらい考えてるけど、とりあえずはOperaメインで風博士をLDR用にして様子を見る。
]]>SDKを落として解凍してgoogle_appengineディレクトリに移動する。
# unzip google_appengine.zip # cd google_appengine
python 2.4.5には対応してないといわれたので2.5を入れる。/usr/bin/pythonが/usr/bin/python2.4へのシンボリックリンクになってるので、dev_appengine.pyのshebangを/usr/bin/env python2.5に書き換え。書き込み権限がないのでvimで:w!して強制書き換え。んで再度実行。
# sudo apt-get install python2.5 # (略) ~/google_appengine # head -n 1 ./dev_appserver.py #!/usr/bin/env python2.5 ~/google_appengine # ./dev_appserver.py demos/guestbook/
localhost:8080を見てみると稼働してる。
次にGetting StartedのHello World を試してみる。google_appengine/demos/testにディレクトリを作り、hw.pyとapp.yamlをそれぞれつくる。
# cat app.yaml application: hel version: 1 runtime: python api_version: 1 handlers: - url: /.* script: hw.py # cat hw.py print "Content-Type: text/plain\n\n" print "hew"
さっき起動したdev_appserver.pyをCtrl+Cでいったん落として、./dev_appserver.py demos/test/で再起動。localhost:8080にhewって出た。applicationがどこに反映されたのかわからないけど、Googleに持っていったときに使うらしい。要するにfoo.appspot.comを使うならapplication: fooって設定しないといけないらしい。
まずGoogle App Engineのユーザー画面からアクティブにする。
続いて./appcfg.py update demos/test/を実行すると、ユーザー名(メアド)とパスワードをきかれるので入力する。あとは自動でアップロードとかをやってくれて終了。
App Engine上で本気出すのは中長期的に考えてちょっと怖いので、高可用性と高応答性を生かしたハブ的なものが適してそう。キャッシュサーバとか。ひどい使い方だな。
]]>すこすこって感じの打鍵感が軽くてよいです。音も静かめ。本体がずっしりしてるので安定感も充分。
なんとなくRealforceに似てるけど、Realforceの打鍵音が「ぽこぽこぽこ」なのに対し、libertouchは「かすかすとこ」って感じ。
ニュアンスだけで書いてますけど、伝わるわけがないですね。あんまり詳しくないのでこれくらいが限界です。
]]>今回のターゲットはこれ。
いちおう英語の勉強の一環なので誤訳はないつもりですが、意訳や妙訳や超訳は多分に含まれています。ご指摘いただければとてもありがたいです。
A person who never made a mistake never tried anything new.
彼が失敗することはないだろう。新しいことにチャレンジしていないのだから。
失敗したくない、あるいは失敗が許されないのであれば新しいことに手を出すな。
関連語:フィーチャーフリーズ
Most people don’t try new things because of their fear of failure. Failing is not something to be afraid of. It is often the losers who learn more about winning than the winners. Our mistakes always give us opportunities to learn and grow.
多くの人は失敗を恐れて新しいことに挑戦しない。失敗は怖くない。敗者が勝者より多く学ぶこともある。失敗とは学習と成長のための機会である。
Education is what remains after one has forgotten what one has learned in school.
学校で学んだことをすべて忘れたあとに残るのが教育である。
忘却能力がなければ教育とそうでないものを区別できない。あるいは混同する。博学はまだ判断ではない。
30 years from now, you won’t possibly remember what chapters you had in your science book; you’d only remember what you learn on your way. Life lessons stay with you forever. Real education starts from within.
30年経ったら科学の教科書にどんな単元があったかすら思い出せないだろう。思い出せるのは自分が独自に学んだことだけだ。人生の教訓は常に自分とともにありつづけ、真の教育はそこから始まる。
I am enough of an artist to draw freely upon my imagination. Imagination is more important than knowledge. Knowledge is limited. Imagination encircles the world.
私は想像を自由に描けるという点でアーティストだといえる。想像は知識よりも重要だ。知識は有限だが、想像は世界を取り囲む。
百聞は一見に等しいらしいが、一見は一妄想に劣る。あるいは、一を聞いて十を知り百を妄想する。
When you reflect on how far we humans have come from the prehistoric caves to mind-blowing technological advancements, you would feel the power of imagination. What we have now was built from the imagination of our forefathers. What we will have in future will be built from our imagination.
人類が原始時代の洞窟からどれくらい遠くに来たかと思いを馳せると、イマジネーションの力を感じられるだろう。我々の祖先はイマジネーションによって現代の生活を築き上げた。我々もまたイマジネーションによって未来を築くだろう。
The secret to creativity is knowing how to hide your sources.
創造性の秘密? それは出所の隠しかたを知ることだ。
パクリだとバレないように創造性を発揮しろ。
Creativity and uniqueness often depends on how well you hide your sources. You can get inspired and influenced by other great people; but when you are on stage with the whole world watching, you must become a unique, individual force that learnt different values from different people.
クリエイティビティとユニークさはしばしば、いかに上手に元ネタを隠せたかに依拠する。あなたが誰か偉大な人間に感激したり感化されたりしたとする。そこであなたは世界中の人々が監視するステージに立った。あなたは他の人々とは違うということを学び、ユニークでなければならない。
(なんでユニークさの話になるのか不明。アメリカだからか?)
The value of a man should be seen in what he gives and not in what he is able to receive. Try not to become a man of success, but rather try to become a man of value.
人の価値は、彼がどれだけのものを得られるかではなく、どれだけのものを与えられるかで見るべきだ。成功した人ではなく、価値ある人を目指すべきだ。
何もくれないご主人様に価値はない。
If you think of all the top people in the world, they would have added something of value to the world. You must give in order to take. When your purpose is contributing or adding value to the world, you will be elevated to a higher level of living.
トップクラスの人々を考えると、彼らは世界に対して何かを与えた人だと気づく。何かを得たらそれを適切に与えるべきだ。寄付や貢献を目的とするなら、より上位の生活へと昇っていけるだろう。
There are two ways to live: you can live as if nothing is a miracle; you can live as if everything is a miracle.
生活するには2通りの方法がある。奇跡なんてないと思うか、何もかも奇跡だと思うかだ。
日本では昔、ただの偏西風を神風とかいって盛り上がりましたね。と書いてみて偏西風だったかどうか気になったのでググったら、冬将軍と神風がイチャイチャしてるという妄想を発見してさすが世の中は広いなと思いました。
というわけで教訓は、奇跡の有無にかかわらず世の中って広い。
When nothing is a miracle, you gain the power of doing anything you want and you have no limits. And when everything is a miracle, you stop by to appreciate even the smallest of beautiful things in the world. Thinking both ways will give you a productive and happy life.
もしも奇跡がなかったら、したいことを実現するための力を得るが際限がない。もしもすべてが奇跡なら、ちょっとしたことに多大な価値を見出すだろう。どっちがあなたを生産的にするか、あるいは幸福にするか考えてみよう。
When I examine myself and my methods of thought, I come to the conclusion that the gift of fantasy has meant more to me than any talent for abstract, positive thinking.
自分の思考法や自分自身について考えを巡らせてみると、目の前に吊るされたニンジンはどんな才能よりも活躍してくれているという結論に至った。要するに、攻めの姿勢だ。
すっぱいブドウよりすぐにでも取れそうなニンジンを。
Dreaming about all the great things that you can achieve is the key to a life filled with positivity. Let your imagination run amuck and create the world that you would wish to be in.
自分で出来る範囲のささやかな成功を夢見ることは、積極的に生活するための鍵だ。イマジネーションを縦横無尽に走らせ、自分が居たいと思う世界を創造しよう。
In order to be an immaculate member of a flock of sheep, one must above all be a sheep oneself.
一点の曇りもない羊の群れの一員であるためには、まずは何よりも羊でなければならない。
のび太のくせに生意気だ。
疲れてきたので省略。なんか起業家がどうとか書いてある。
You have to learn the rules of the game. And then you have to play better than anyone else.
ルールを学べ。より良く振る舞え。
KY。
ていうか空気はルールじゃないな。あとここではKYを「空気読め」の命令形で使ってるけど、いつの間にか「空気読めない」という状態説明になったのはなんでだ。
略
The important thing is not to stop questioning. Curiosity has its own reason for existing.
質問を止めるな。好奇心はそのためにある。
勢いあまって質問に質問を返すと吉良吉影に爆破されるので、やっぱり好奇心は猫をも殺すんじゃないかと思いました。
半分くらいでだいぶ疲れた。8以降の手抜きっぷりがまぶしい。
英文読む→意味がわからない単語を辞書で引く→文意がわからなければ一文まるごとExcite翻訳へ突っ込む→意味を理解する→日本語でその意味を記述する→教訓をでっちあげる
というプロセスを繰り返した。でっちあげた教訓のなかでは1の逆説と4の循環が好き。
よくできたのか間違ってるのかよくわからない訳たち。
| I am enough of an artist 〜 | 私は〜という理由でアーティストといえる |
| gift of fantasy | 目の前に吊るされたニンジン |
| positive thinking | 攻めの姿勢 |
| positivity | 積極的に |
| adding value to the world | (世界への)貢献 |
何がなんでも「ポジティブ」って言葉を使いたくなかったせいもある。
]]>結論としては、etchではMACアドレスがどうとかでネットワークの設定か.vmxファイルをいじらないといけないみたい。
/etc/udev/rules.d/z25_persistent-net.rulesを確認するとeth0とeth1があって、eth1は.vmxファイルと同一のMACアドレスが書いてあった。sudo vim /etc/network/interfaceして、eth0をeth1に書き換えたあとOS再起動すると無事ネットワークがつながった。このやり方だとまた.vmx側のMACアドレスが変わって意味ない気がするけど、原因と対策は得られたのでまあいいか。
]]>デザイン&感性情報の皆様
春を迎え、お花見のシーズンがやってまいりました。
今年もデザイン&感性のお花見を開催したいと思っています。
って文面で始まるメール。新入生は参加費無料らしい。
Fromは@kansei.tsukuba.ac.jp、Toが@mail2.accsnet.ne.jpで、CCやToにも変なところはない。スパムにしては商売っ気がなさすぎるし、僕と筑波大学には何のつながりもない。あとkansei.tsukuba.ac.jpは筑波大学の感性情報学科?かなんかのドメインらしい。
ということでFromの筑波大生の人に「なんか知らんけどうちにメールきたから操作ミスってないか確認してみたら?」ってメールを返した。
その翌日、Fromの人から返信がきて「あーそうだったような気がする迷惑かけたすまない」「気にするな」「これも何かの縁ですしていうか人数集まらないんでもしお暇なら来てみませんか」「じゃあ行く」「場所は最初のメールにも載ってますけどあれなら迎えに行きます」「駅まで来てくれ頼んだ」てことで花見に参加して電車の中でこれなんてYou’ve got mailだとか考えてたら駅に着いてFromの人と対面してToの人はあの人ですよあどうもToの人こんにちはあその節はどうもとか挨拶しながらやっぱり大学生は若いなあと感じて酒飲んで仕事の話が新鮮に聞こえるみたいなのでいろいろ話してる最中に、当該メールのヘッダ1行目にDelivered-To: があるのを見つけてしまったので現実によって妄想が急冷されてToの人がメール転送設定ミスっただけだろうってことでいろんな意味で落着した。
Delivered-Toとか死ねばいいのに。妄想を返せ。SMTP爆発しろ。Toの人のミスなんだったら、Fromの人のミスだということで成り立っていた妄想がすべて瓦解するわけでそのへんまた最初から設定し直さないといけないから上半身裸で桜の木によじ登ってた人の行方とかが迷宮入りしてしまって続きが気になってしょうがない。現実と明らかに矛盾する設定の妄想を続けられるほど妄想スキルが高くないのでもう無理だ。誰かにrm -rf /された気分だ。がーーー。
とか考えながらメールに返信すらしていません。ちなみに返信期限は4月2日なのでToの人は花見に参加できませんでした。たぶん。
]]>今回は意味わからんクレーム2号の人に教えたら大好評だったAutoPagerizeのご紹介です。
ページを下へスクロールするだけで、自動で次のページを継ぎ足してくれるものです。前に上げた動作サンプルを再掲。
この動画では
でそれぞれ実行されています。他にも各ニュースサイト、ニコニコ動画のランキングページ、YouTube検索結果、Amazon検索結果、はてなブックマークなどいろんなサイトに対応しています。
クロスブラウザ対応bookmarklet が公開されてます。これをブラウザに登録して、Googleの検索結果で実行してみるとどういうものか体験できます。
毎回bookmarkletを実行するのは面倒。ページを開いたら勝手に実行して欲しい。
そういうときはインストールして使いましょう(というかこの使い方が本来)。素のIE6では出来ません。
インストール手順はFirefox,Opera,Safari,Sleipnirなどなどでそれぞれ異なって来ますが、やってることは「特定のJavaScriptをローカルに保存してページ読み込むたびに自動実行してる」だけです。
別にフォルダ作る必要はないんですが、デフォルトだとC:\Document and Settings\username\Application Data\.....とか奥深く過ぎて意味がわからなくなるので、あえてC:\userjsということにしました。
もちろんD:\userjsでもC:\opera\aaaでもなんでもいいです。
Mac持ってないのでSafariはよくわかりませんが、GreaseKitとかいうのを使うらしいです。Windows版Safariだと使えないみたい。
http://d.hatena.ne.jp/os0x/20080109/1199849910 を参考に試してみてください。
Sleipnirも1.66以降ほとんど触ってないのでよくわかりませんが、SeaHorseを使って出来るみたいです。
http://furyu.tea-nifty.com/annex/2008/03/autopagerlike_s_56ec.html を参考に試してみてください。
IE6だと不可能ですが、IE7だとIE7pro経由でできるみたいです。
AutoPagerizeと関係ない余談ですが、IE7はIE7proを入れてはじめて真っ当なブラウザになれるので、IE7を使ってるけどまだ入れてないって人はインストールしておくといいと思います。マウスジェスチャとか付きます。個人差はありますが、これでSleipnirの5〜8割くらいの便利さを享受できます。
| 使用前 | 使用後 |
|---|---|
| 「次へ」のリンクが押しづらい | そもそも押す必要がない |
| 次のページへ行き過ぎてどれだけ前のページに戻ったらいいかわからない | 上にスクロールする、ページ内検索する |
| Googleの表示件数を100件にしないと探すの面倒だけど、そのせいでちょっと遅くなった | Googleの表示件数を10件にしてデフォルトの軽さを取り戻す |
| ニュースサイトで記事が10ページに分けられてるのが果てしなくうっとうしい | 心穏やかな毎日 |
他にも何か便利な使い方や体験談、心温まるエピソードなどがあればぜひ教えてくださいませ。
]]>マイグレーションは面倒そうだったのでまずはO/Rマッピングから攻めていくことにする。
sudo apt-get install ruby1.8-dev sqlite3 とかsudo gem install sqlite3 active_recordしておく。準備が出来たらこう書く。
require "rubygems"
require "sqlite3"
sql=<<SQL
CREATE TABLE students (
id INTEGER PRIMARY KEY NOT NULL UNIQUE,
test_text TEXT NOT NULL DEFAULT 'default_text',
aa INTEGER NOT NULL DEFAULT 0
);
SQL
SQLite3::Database.new("aa.db").execute(sql)
| students |
|---|
| id |
| test_text |
| aa |
これくらいならわかる。SQLite3クラス(モジュール?)が他に何を出来るかは知らないけど、newでデータベース選択、executeでSQL実行がわかればそれでいいや。
ちなみにSQLiteでINTEGERかつPRIMARY KEYなフィールドは、MySQLでいうauto_increment相当になる。
require "rubygems"
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3", :database => "aa.db"
)
class Student < ActiveRecord::Base
end
Student.create({:aa => rand(100)})
p Student.find(:all)
p Student.find(:all,:conditions => "aa < 100")
p Student.find(:all,:conditions => ["aa > ?",50])
p Student.find(:first)
s=Student.find(:first)
s.test_text = "huhahahahahah"
s.save
p Student.find(:first,:conditions => "test_text <> 'default_text'")
Student.find(:first).destroy
まず最初にestablish_connectionでDB接続。続いてStudentクラスの定義、というか、これはクラス名を使って裏側でいろいろな処理をしてくれるので空でいい。楽だ。
Student.createでINSERT相当。
Student.find(:all)でSELECT * FROM students相当。find(2)ならWHERE id=2が付く。ActiveRecorde::Base#find のAPIはだいたいこんな感じ。
Student.find(:first)の返り値のプロパティがそのままカラムになってるので、s.test_textに適当な値を入れてsaveするとUPDATE相当。
destroyでDELETE相当。
これでひととおりのCRUDはわかった。
ファイルを改めて、ここ を参考にまずwith_scopeを試してみる。
require "rubygems"
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3", :database => "aa.db"
)
class Student < ActiveRecord::Base
end
p Student.find(:all)
Student.with_scope(:find => {:conditions => "id < 8"}){
p Student.find(:all)
}
実行するとidが8未満のやつだけが列挙されるはず。
/var/lib/gems/1.8/gems/activerecord-2.0.2/lib/active_record/base.rb:1532:in `method_missing’: protected method `with_scope’ called for Student(id: integer, test_text: text, aa: integer):Class (NoMethodError)
えーprotected? 仕様変更? まあ意図はわからんでもないので別にいいけど、コピペ学習が止まるのは困るので公式 からサンプルをあさる。
require "rubygems"
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3", :database => "aa.db"
)
class Student < ActiveRecord::Base
def self.scope_test
with_scope(:find => {:conditions => "id < 8"}) {
find(:all)
}
end
end
p Student.find(:all)
p Student.scope_test
ん、OK。
公式のサンプルにもある:createがよくわからないので、勘で試してみる。
class Student < ActiveRecord::Base
def self.scope_test
with_scope(:find => {:conditions => "id < 8"}) {
find(:all)
}
end
def self.scope_test_create
with_scope(
:find => {:conditions => "id < 8"},
:create => {:test_text => "scope test"}
) {
create({:aa => 999})
}
end
end
p Student.find(:all)
p Student.scope_test
p Student.scope_test_create #<Student id: 15, test_text: "scope test", aa: 999>
なるほどなるほど。なんかインデントが気持ち悪くなったけど意味はわかった。そのスコープ内でcreateするときのデフォルト値みたいなもんだな。
続いてscopeのネスト。
def self.scope_test_nest
with_scope(:find => {:conditions => "id < 30"}){
with_scope(:find => {:conditions => "aa < 40"}){
find(:all)
}
}
end
でid < 30かつaa < 40なスコープが定義できたのを確認。
「リレーショナル」と前出の「華麗なる」が地味に韻を踏んでいる点に注意。
リレーション(公式にはアソシエーションらしい)は要するにhas_manyとかbelongs_toとか、一時期よく見かけたやつですね。
どうでもいいけどbelongって単語を見ると未だにall your base are belong to us が頭に浮かぶ。いい加減記憶をアップデートしたい。
require "rubygems"
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3", :database => "aaa.db"
)
class Student < ActiveRecord::Base
belongs_to :group
end
class Group < ActiveRecord::Base
has_many :students
end
if !Student.find(:first) # => nil
10.times{|n|
Student.create({:aa => n, group_id => n})
Group.create({:group_name => "group #{n}"})
}
end
p Student.find(:all)
の前にまずgroupsテーブル作らないと。どうせならってことでaa.dbじゃなくてaaa.dbを新規作成する。
require "rubygems"
require "sqlite3"
sql=<<SQL
CREATE TABLE students (
id INTEGER PRIMARY KEY NOT NULL UNIQUE,
test_text TEXT NOT NULL DEFAULT 'default_text',
group_id INTEGER ,
aa INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE groups (
id INTEGER PRIMARY KEY NOT NULL UNIQUE,
group_name VARCHAR NOT NULL DEFAULT '',
ggg INTEGER NOT NULL DEFAULT 0
);
SQL
SQLite3::Database.new("aaa.db").execute(sql)
| students | groups |
|---|---|
| id | id |
| test_text | group_name |
| group_id | ggg |
| aa |
な感じ。groups LEFT OUTER JOIN students ON (students.group_id = groups.id)される予定。
んで先のコードを実行すると
/var/lib/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/sqlite3_adapter.rb:29:in `table_structure’: Could not find table ‘groups’ (ActiveRecord::StatementInvalid)
なんでやねん。
groupsテーブルがないと言われて考えられることはgroupsテーブルがないってことなので、ちょっと考えてCREATE TABLEのほうをこうしてみた。
require "rubygems"
require "sqlite3"
sql=<<SQL
CREATE TABLE students (
id INTEGER PRIMARY KEY NOT NULL UNIQUE,
test_text TEXT NOT NULL DEFAULT 'default_text',
group_id INTEGER ,
aa INTEGER NOT NULL DEFAULT 0
);
SQL
SQLite3::Database.new("aaa.db").execute(sql)
sql=<<SQL
CREATE TABLE groups (
id INTEGER PRIMARY KEY NOT NULL UNIQUE,
group_name VARCHAR NOT NULL DEFAULT 'alone',
ggg INTEGER NOT NULL DEFAULT 0
);
SQL
SQLite3::Database.new("aaa.db").execute(sql)
んで再度実行すると、きちんとStudent.find(:all)が動いた。「;」で区切って複数同時に実行しようとしてもダメみたい。よく考えたらDatabase.newなんだから当然か。
何やってたっけ。ああそうそうbelongs_toとかか。
これ を参考にいろいろ試してみることにする。
g=Group.find(1)
p g.students # => [#<Student id: 1, test_text: "default_text", group_id: 1, aa: 5>]
puts g.students.size # => 1
s=Student.create({:test_text => "tesuto"})
g.students << s
p g.students # => [#<Student id: 1, test_text: "default_text", group_id: 1, aa: 5>, #<Student id: 11, test_text: "tesuto", group_id: 1, aa: 0>]
puts g.students.size # => 2
g.students.create({:test_text => "tset2"})
puts g.students.size #=> 3
だいたいわかった。1対1も多対多も似たようなものだろう。
SQLを書きたい子なのでO/Rマッピングには消極的なほうですが、これはたしかに便利だなあ。ていうか、あんなにひどいテーブル定義かいといてSQL書きたいもないな。
]]>今日はだいぶ気持ち悪いJavaScript を書けて満足。Rubyの勉強のためにと思ってたけど、MVCとかなんとかきっちりしたコーディングに嫌気がさしたときの鬱憤晴らしにも活躍しそうだ。
それにしてもまだまだお題の意味がわからなかったりするのでダメだなあ。
tt25にするつもりが間違えてtt23にしてしまったのでタイトルはmisnamed。tumblrといえばエロ画像とかグロ画像とかってイメージがあるけど、まあなんかてきとうに。
Tumblr公式bookmarkletをOperaのニックネーム付きブックマークに入れて、Shift+F2 t(umblr)でreblogできるようにしといた。Shift+F2でbookmarklet使うのは、WindowsでいうWin+Rの高級版(MacでいうとQuicksilver? )みたいな感触でわりと好き。
LDRize(というかMinibuffer)はなんかもっさりしてるので使うの諦めた。
]]>テスト通過よりも、Safari/Webkitのチート100点 について、前例 もあることだし「またか」って感じではあった。Appleのページに置いてあるグラフも、信用できないテストスイートの結果 だし。数年前からやってることがまったく進歩してない。
まあ、いわれてみればSafari/Webkit陣営がWeb標準とか主張してるのあんまり見たことないから純粋に宣伝とかマーケティングのための開発であって、その戦略においてチート100点とか空実装とかあれとかこれとかは実に合目的的だ。爆発してしまえ。
Windows版Safariもなんか得体のしれないことをやってるんだろうな。Appleのリンゴには農薬が残留していますが死にはしませんし何よりきれいです、みたいな。
]]>デベロッパはMercurialを使うことができる。多くの調査の結果同氏がいきついたバージョン管理システムがMercurialである。
http://journal.mycom.co.jp/articles/2006/05/15/bsd4/001.html
このようにFreeBSDデベロッパも認める 地上最強のバージョン管理システムことMercurialを試してみた。
インストールはいつもどおりsudo apt-(ry
Subversionはリポジトリを本家と分家に区別して分家が本家にマージされていくモデルだけど、Mercurialは本家と分家の区別がなく本家のコピーも本家であると主張する。
SFの諸概念でいうとSubversionがタイムパラドックスに悩む一方、Mercurialはパラレルワールドを構築する。
インストール直後の全然まったく完璧に純粋無垢な初期状態で
hg initで今いるディレクトリにリポジトリ構築hg commit -A -m "コメント" でコミットhg update -r 2 でリビジョン2へロールバック、hg update で最新リビジョンへ更新なんかをコリコリ試すとMercurialのスパっとした操作体系がふよふよと頭に入ってくるはず。
他にも、なんと他の場所にあるリポジトリとsshやhttpで接続してこんなことが出来る。
hg clone ssh://remote.example.com/hg/test test2 でremote.example.comの/hg/testにあるリポジトリをtest2へコピーremote.example.comからコピーしたものであると確認remote.example.comからtest2へ最新版をコピーtest2からremote.example.comへ最新版をコピーコマンドはhgとかhg --helpとか打てば確認できるので軽く試してみませんか。
この行で「移動する」を満たして終了。
「レトリックのすすめ」でマスターしたい12の文彩 をとりあえず全部実装してみた。各行で1個ずつやってる。
]]>正しくは「product?id=12」だけどこれはご愛嬌。
まず原文もそうだけど見出しの語順が逆だと思う。「product/ipod-nano/12.htmlをproduct.php?id=12にリライト」が処理の流れ的に正しい。
で、問題のコードですが、
RewriteEngine on RewriteRule ^product/([a-zA-Z0-9_-]+)/([0-9]+).html$ product.php?id=$2
元記事のコメント欄で突っ込まれてる とおり、product/awdftgyhjiiko/89818917897871.htmlとか、product/microsoft-zune/12.htmlまで同じ内容になるのはさすがにダメだろう。
RewriteEngine on RewriteRule ^product/ipod-nano/([0-9]+).html$ product.php?id=$1
か
RewriteEngine on RewriteRule ^product/([a-zA-Z0-9_-]+)/([0-9]+).html$ product.php?genre=$1&id=$2
が正しい。
あとこの例だとmod_rewrite使わなくても、
Options +MultiViews
とするとproduct.php/ipod-nano/12.htmlやproduct/ipod-nano/12.htmlがそのまま通るので、PHP側で受け方をちょっと変えればrewriteしなくてもいい。404とかを手動でやらないといけないから面倒だけど、rewrite使えない環境もあるだろうから考慮に入れといて損はないかと。
さらに蛇足だけどproduct/ipod-nano/12.htmlてのはURLの設計がおかしいと思う。これがproduct/ipod-nano/specとか、product/12.htmlとか、product/ipod-nano.htmlならわかる。12って何のパラメータなんだ? あとせっかく()でキャプチャしてるわりにPHPには渡してないので「ipod-nano」は不要なんじゃないのか?
RewriteEngine On RewriteRule ^([a-zA-Z0-9_-]+)$ user.php?username=$1 RewriteRule ^([a-zA-Z0-9_-]+)/$ user.php?username=$1
間違いではないけど冗長。
RewriteEngine On RewriteRule ^([a-zA-Z0-9_-]+)/?$ user.php?username=$1
で充分。
あと元記事でもコリスでもSEOについて触れてるわりに、これだとパーマリンクが/の有無でばらけてしまってSEOが甘い。3行目を削除して、/ありURLをNot Foundにしてしまってもいいけど、ちゃんと書き直すとこう。
RewriteEngine On RewriteRule ^([a-zA-Z0-9_-]+)$ user.php?username=$1 RewriteRule ^([a-zA-Z0-9_-]+)/$ $1 [R=301]
最後に/がついてるURLにアクセスしてきたら/なしのURLに301リダイレクトして一本化する。このブログでもそうしてる。
例:test.com/new
RewriteEngine On RewriteCond %{HTTP_HOST} ^test.com$ [OR] RewriteCond %{HTTP_HOST} ^www.test.com$ RewriteCond %{REQUEST_URI} !^/new/ RewriteRule (.*) /new/$1
それだと最終的にtest.com/new/newを見に行くことになるけどあってる?
たぶん元々の意図は、「http://test.com/old-permalinkへのアクセスを、内部的にhttp://test.com/backup/old-contentにリライトしたい(古いコンテンツを全部backupディレクトリに移してしまいたい)」なんだと思うけどあんまり自信ない。
あ、違うか。逆だ。元記事だとこう書いてるな。
Suppose the you’ve redeveloped your site and all the new development reside inside the “new” folder of inside root folder.Then the new development of the website can be accessed like “test.com/new”.Now moving these files to the root folder can be a hectic process so you can create the following code inside the .htaccess file and place it under the root folder of the website. In result, www.test.com point out to the files inside “new” folder.
↑適当訳:サイトをリニューアルして、新しいファイルをnewディレクトリに置いたとしよう。これで一応新しいサイトにはhttp://test.com/new/でアクセスできる。そこから新しいサイトのファイルを一番上のディレクトリに移していくなんていう死ぬほどめんどくさい作業をしなくても、以下のように.htaccessを書けばほぼ同じことができる。要するに、http://test.com/へのアクセスをhttp://test.com/new/へとリライトするわけだ。
RewriteEngine On
RewriteCond %{HTTP_HOST} ^test\.com$ [OR]
RewriteCond %{HTTP_HOST} ^www\.test\.com$
RewriteCond %{REQUEST_URI} !^/new/
RewriteRule (.*) /new/$1
か。なるほど。rewriteを使って、/public_html/じゃなくて/public_html/new/をルートにするわけか。それだと古いコンテンツへのリンクがたぶん死ぬけど、まあリニューアルなんだし仕方ないか。http:/test.com/new/へのアクセスがトップページと同じ内容になるけど、秘密にしとけば実用的には問題ないか。
あとRewriteCondのHTTP_HOSTチェックはこの例の場合どうでもいいと思う。
mod_rewriteとか使わないに越したことはない。
膨大な設定例やドキュメントがあるにもかかわらず、mod_rewrite は黒魔術である。かなりイケてるっぽい黒魔術だが、やっぱり所詮は黒魔術である。
プログラム界隈で黒魔術というと、「難解だけどよく動いてるコード」「ダークサイドに堕ちた 」などなど、強力だけど普段使いするな、リスクを理解してから使え、みたいなニュアンスです。カリカリにチューニングされたコードとか、極限まで短くされたワンライナーなんかが該当します。汚さと引き換えに強力なパワーがどうとか。ただのぐちゃぐちゃなコードはスパゲッティとか呼ばれて、黒魔術とは区別されます。
Cool URI の考えに始まり、SEOが云々言い出して、Railsが突貫して以降、rewriteは当たり前のものみたいになってるけど、静的ページがメインのサイトで必要になることはまずないはず。最後の例の/new/みたいに不用意に使えばユーザーと管理者が無駄にメダパニるだけなので、そのへん注意して用法を誤らず用量を適正に保ちつつ清く正しく使わないとそのうち誰かが流れ弾に当たって死にます。
]]>手順は、
で終わり。これはSubversionの機能。
update前後で気づいた違いは「問題」が「チケット」になったくらい。URLはissueのまんまだったけど、テキストは全部「チケット」で統一されてた。言語設定を「英語」にするとIssuesとかNew issueだったので翻訳が変わっただけみたい。トラッカーの「担当」が変わってるか見てみたかったけど、自分で名前変えてしまったのでどうなったかは知らない。
]]>RackでWebアプリのWebサーバー依存を無くす とかRack を使って Web サーバで統一されたインターフェイスの利用する を参考にしてとりあえず動かしてみた。
rackとかmongrelとか。
# sudo gem install mongrel rack
require "rubygems"
require "rack"
include Rack
hw=Proc.new {|env|
req=Request.new(env)
Response.new.finish{|res|
res.write "hello,world!"
res.write req.GET.to_s
}
}
apps=URLMap.new([
["/",he]
])
Handler::Mongrel.run apps,:Port=>10081
http://localhost:10081/?aa=gaeijaijとか見てみて、ちゃんとGETパラメータが取れてるのを確認できた。日本語もおk。
「2007グラビアアイドル ランキング」にランキング入りしたグラビアアイドル全画像 にインスパイアされてなんとなくつくった。
暇だったのでつい。途中で飽きたのでピンを開いたりはできないですしIE無視してますし都道府県もうろ覚えなのでそのうち消えます。
Flickrからアルバムを生成するPythonコード とflickr apiのドキュメント を大いに参考にしました。
=begin
flickr=Flickr::Photos::Search.new({
"api_key"=>"APIKEY",
"format"=>"json",
"nojsoncallback"=>true,
"per_page"=>50,
"text"=>keyword,
"sort"=>"date-posted-desc",
})
flickr.to_json(keyword)
flickr.to_json(keyword,callback)
flickr.to_a(keyword)
=end
require "open-uri"
require "cgi"
require "rubygems"
require "json"
module Flickr
REST_URL="http://api.flickr.com/services/rest/?"
class Photos
class Search
PHOTO_URL="http://farm%s.static.flickr.com/%s/%s_%s.jpg"
PERMALINK="http://www.flickr.com/photos/%s/%s"
def initialize hash
hash["method"]="flickr.photos.search"
@hash=hash
end
def fetch id
cache="cache/#{id}.json"
if File.exist?(cache) == false
q=@hash.map{|k,v|
CGI::escape(k.to_s)+"="+CGI::escape(v.to_s)
}.join("&")
url=Flickr::REST_URL+q
puts url
data=open(url).read
File.open(cache,"w"){|fp|
fp.puts(data)
}
else
data=open(cache).read
end
data
end
def to_json id,callback=nil
json=to_a(id).to_json
if callback
"#{callback}(#{json})"
else
json
end
end
def to_a id
data=fetch(id)
result=JSON.parse(data)
(result["photos"]["photo"] || []).map {|p|
{
:img=>sprintf(PHOTO_URL,p["farm"],p["server"],p["id"],p["secret"]),
:link=>sprintf(PERMALINK,p["owner"],p["id"]),
:title=>p["title"],
}
}
end
end
end
end
]]>そのうち公式に対応するだろうから、ウェブアプリ化する気もPHPで書く気もない。あとなぜか「自殺掲示版」タグを上手く取って来れない。
=begin
require "thatsping.rb"
tag=CGI::escape("foobar")
path="/path/to/save/{tag}.xml"
body=Thatsping::tag(tag)
File.open(path,"w"){|fp|
fp.puts(body)
}
=end
require "open-uri"
require "cgi"
require "rubygems"
require "hpricot"
class Thatsping
BASE_URI="http://thatsping.com/"
def tag tag
Thatsping::tag(tag)
end
def Thatsping::tag tag
url=Thatsping::BASE_URI+"tag/"+tag
result=[]
open(url){|f|
body=f.read
Hpricot(body).search('//div[@id="blue"]/dl/dt'){|link|
tmp={}
tmp[:link]=CGI::escapeHTML(link.at("a").attributes["href"])
tmp[:title]=CGI::escapeHTML( link.at("a").inner_html )
link=link.next_sibling
tmp[:tags]=link.at("span").search("/a").to_a.map{|n|
CGI::escapeHTML(n.inner_html)
}
result << tmp
}
}
feed=<<FEED
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xml:lang="ja">
<channel>
<title>thatsping::#{tag}</title>
<link>#{url}</link>
FEED
result.each{|v|
feed+="
<item>
<guid isPermalink=\"true\">#{v[:link]}</guid>
<title>#{v[:title]}</title>"
v[:tags].each{|t|
feed+=" <category>#{t}</category> "
}
feed+="</item>"
}
feed+="</channel></rss>"
end
end
やっぱりなんかスマートじゃないなあ。
]]>軽い。往年のOpera6くらい軽い。それでいてGmailやGoogleマップも動くので実用面での不備はほぼないと思われる。
Apple製品は油断するとQuickTimeとかBonjourとかQuickTimeとかQuickTimeを入れられるのでインストールから気が抜けない。案の定Bonjourをインストールするか聞かれたので速攻で拒否しておいた。Bonjourってなんだろう。
インストール完了後にそのまま起動したら、現在Safariは既定のブラウザではないが既定にするか? といつもの質問をされた。「いいえ」を押そうとして右下にカーソルを動かしたが右下は「はい」だったので危うく押すところだった。Macの常識とはいえWindows下においてはこれも罠だ。
AutoPagerizeがないとブラウジングする気が7割減なのでSafari用User Script制御アプリのGreaseKit (FirefoxでいうGreasemonkey)を入れようとしたら、ダウンロードできるものがdmgしかなかった。dmgが何なのかよくわからないけどMac向けの何かであることはわかる。「greasekit windows」でググると失敗した旨を告げるtwitter が出てきたので素直に諦めた。
bookmarklet版 はMnibufferへの入力がなぜか大文字になるのと、LDRizeで立てたピンを開くとタブじゃなくウィンドウで開いた以外は動いたけど、うーん。

あと個人的な問題点として、マウスジェスチャができない(別アプリを入れてSafariと関連付けて設定したらできるらしい)、タブが縦に置けない&多段表示できない、検索エンジン追加がわからん、などなど。
Ctrl+Fで検索、Ctrl+Tで新規タブ、Ctrl+E / Ctrl+Kで検索窓にフォーカス、Ctrl+Lでアドレスバーにフォーカス、Ctrl+Wでタブ閉じるのは確認した。だいたい互換性は考えてあるっぽい。
4段階の強度があって、デフォルトだと2番目に強い「中」なんだけど、3番目の「弱」ないし4番目の「標準」がいちばん読みやすいと思う。「中」だとたまに文字がつぶれる。
Safari3 アンチエイリアス「標準」MS Pゴシック

Safari3 アンチエイリアス「中」MS Pゴシック(デフォルト)

なんかこうやって見ると「中」と「標準」の違いがわかりにくいな。実際に変更するとわりとはっきり違うので動画してみたけど、あんまり変わらないかも。VNC越しだしなあ。
いちおう以下参考。
XP IE6

Linux Opera(xfce/IPA P ゴシック)

Macは持ってないので知らないけど、Linuxと同等かそれ以上にきれいだったはず。
あとアンチエイリアスのせいか配色ミスかは知らないけど、ツールバーの文字が死ぬほど見にくい。
IEユーザーに薦めるならFirefoxよりSafari3のほうが向いてるかと思います。
]]>PHPUnitのドキュメント を参考にまずはインストール。バージョンは3.2。
# pear channel-discover pear.phpunit.de # sudo pear install phpunit/PHPUnit # phpunit -v PHPUnit 3.2.15 by Sebastian Bergmann.
これの一番上にあるサンプルコード で動作テスト。
# vim ArrayTest.php # phpunit ArrayTest
ちゃんと動くのを確認できたら完了。
最初、ググって出てきたv2.3のドキュメント見ながらやってたらエラー出まくって大変だった。
まずCommentsクラスとCommentsTestを作る。
# cat Comments.php
<?php
class Comments {
}
# cat CommentsTest.php
<?php
require_once "PHPUnit/Framework.php";
class CommentsTest extends PHPUnit_Framework_TestCase {
}
とりあえず思いつくままCommentsのほうにメソッドを定義していく。
# cat Comments.php
<?php
class Comments {
function __construct($permalink){
$this->permalink=$permalink; // コメント先記事のIDみたいなもの
$log=$this->permalink.".txt";
if(!is_file($log)){
touch($log);
}
}
function push($comment){
}
function remove($commentId){
}
function find(){
}
}
次にとりあえずfindメソッドのテストを書いてみる。
どのテストにせよCommentsオブジェクトが必要なので、最初に作って使いまわしたりしたいところだ。そういうときはsetUpメソッドの中でやるといいらしい。
# cat CommentsTest.php
<?php
require_once "PHPUnit/Framework.php";
class CommentsTest extends PHPUnit_Framework_TestCase {
function setUp(){
require_once "Comments.php";
$this->comments=new Comments("dummy/dummy");
}
function testFind(){
$this->assertEquals($this->comments->find(),array());
}
}
ここでPHPUnitを実行してみる。Comments#findは何もしてない(空配列返してない)ので、テストに失敗するはず。
# phpunit CommentsTest.php PHPUnit 3.2.15 by Sebastian Bergmann. F Time: 0 seconds There was 1 failure: 1) testFind(CommentsTest) Failed asserting that Array ( ) matches expected value <null>. /path/to/CommentsTest.php:12
とりあえずComments#findで空配列を返すようにしてみる。
# cat Comments.php
<?php
// changelog
// 1. Comments#find()が空配列をreturn
class Comments {
function __construct($permalink){
$this->permalink=$permalink; // コメント先記事のIDみたいなもの
$log=$this->permalink.".txt";
if(!is_file($log)){
touch($log);
}
}
function push($comment){
}
function remove($commentId){
}
function find(){
return array();
}
}
もっかいテスト。これは上手くいくはず。
# phpunit CommentsTest.php PHPUnit 3.2.15 by Sebastian Bergmann. . Time: 0 seconds OK (1 test)
さて。Comments#pushを書く前に保存形式やら項目やらを考えないといけない。とりあえず名前と本文の2つを持つことにしよう。形式はCSVでいいか。
# cat Comments.php
<?php
// changelog
// 1. Comments#find()が空配列をreturn
// 2. コンストラクタで$this->logに$log
// Comments#push()を実装
// Comments#find()をちゃんと実装
class Comments {
function __construct($permalink){
$this->permalink=$permalink; // コメント先記事のIDみたいなもの
$log=$this->permalink.".txt";
if(!is_file($log)){
touch($log);
}
$this->log=$log;
}
function push($comment){
$fp=fopen($this->log,"a");
fputcsv($fp,$comment);
fclose($fp);
}
function remove($commentId){
}
function find(){
$result=array();
$fp=fopen($this->log,"r");
while($post=fgetcsv($fp)){
$result[]=$post;
}
return $result;
}
}
卒倒しそうなほど適当だけど、まあこんなもんか。続いてテストを書く。ていうかこの順番だとテストファーストじゃないような気がする。まあいいか。
# cat CommentsTest.php
<?php
require_once "PHPUnit/Framework.php";
class CommentsTest extends PHPUnit_Framework_TestCase {
function setUp(){
require_once "Comments.php";
$this->comments=new Comments("dummydummy");
}
function testFind(){
$this->assertEquals($this->comments->find(),array());
}
function testPush(){
$arr=array("name"=>"なまえ","body"=>"どうもどうも");
$this->comments->push($arr);
$this->assertEquals($arr,$this->comments->find());
}
}
There was 1 failure: 1) testPush(CommentsTest) Failed asserting that two arrays are equal. --- Expected +++ Actual @@ -1,5 +1,9 @@ Array ( - [name] => なまえ - [body] => どうもどうも + [0] => Array + ( + [0] => なまえ + [1] => どうもどうも + ) + )
ああなるほど。返り値が二次元配列になって一致しないのはテストのしかたが悪いからともかくとして、添字が失われて数値になるのは困るな。じゃあ、
# cat Comments.php
<?php
// changelog
// 1. Comments#find()が空配列をreturn
// 2. コンストラクタで$this->logに$log
// Comments#push()を実装
// Comments#find()をちゃんと実装
// 3. $this->colsを作ってfind()でarray_combine()するようにした。
// Comments#push()で、ちゃんとname,bodyの順に保存されるように保証。
class Comments {
function __construct($permalink){
$this->permalink=$permalink; // コメント先記事のIDみたいなもの
$log=$this->permalink.".txt";
if(!is_file($log)){
touch($log);
}
$this->log=$log;
$this->cols=array("name","body");
}
function push($comment){
$data=array($comment["name"],$comment["body"]);
$fp=fopen($this->log,"a");
fputcsv($fp,$data);
fclose($fp);
}
function remove($commentId){
}
function find(){
$result=array();
$fp=fopen($this->log,"r");
while($post=fgetcsv($fp)){
$result[]=array_combine($this->cols,$post);
}
return $result;
}
}
ついでにテストのほうもまずかったので直す。
<?php
require_once "PHPUnit/Framework.php";
class CommentsTest extends PHPUnit_Framework_TestCase {
function setUp(){
require_once "Comments.php";
$this->comments=new Comments("dummydummy");
}
function testFind(){
$this->assertEquals($this->comments->find(),array());
}
function testPush(){
$arr=array("name"=>"なまえ","body"=>"どうもどうも");
$this->comments->push($arr);
$this->assertEquals($arr,array_pop($this->comments->find()));
}
}
で、テスト。
1) testFind(CommentsTest) Failed asserting that two arrays are equal. --- Expected +++ Actual @@ -1,9 +1,3 @@ Array ( - [0] => Array - ( - [name] => なまえ - [body] => どうもどうも - ) - )
ん? なぜ今ごろtestFindが。とりあえずもっかい実行してみる。
1) testFind(CommentsTest) Failed asserting that two arrays are equal. --- Expected +++ Actual @@ -1,15 +1,3 @@ Array ( - [0] => Array - ( - [name] => なまえ - [body] => どうもどうも - ) - - [1] => Array - ( - [name] => なまえ - [body] => どうもどうも - ) - )
あー。テストのたびにログが蓄積されていくから、あの適当なtestFind()のほうが通らなくなってるのか。じゃあログを全消去するComments#clearでも作るか。
function clear(){
unlink($this->log);
touch($this->log);
}
で、各テストの最初に呼ぶようにする。ついでにtestClearを追加。
<?php
require_once "PHPUnit/Framework.php";
class CommentsTest extends PHPUnit_Framework_TestCase {
function setUp(){
require_once "Comments.php";
$this->comments=new Comments("dummydummy");
}
function testClear(){
$this->comments->clear();
$this->assertEquals(filesize($this->comments->log),0);
}
function testFind(){
$this->comments->clear();
$this->assertEquals($this->comments->find(),array());
}
function testPush(){
$this->comments->clear();
$arr=array("name"=>"なまえ","body"=>"どうもどうも");
$this->comments->push($arr);
$this->assertEquals($arr,array_pop($this->comments->find()));
}
}
ようやくそれっぽい流れになってきた。次はbody(本文)がないのをpushされたときの処理をテストしよう。変なのが来たらComments#pushはreturn falseするようにして、テストはassertEquals($this->comments->push($invalidArray),false)かな。
function push($comment){
if(empty($comment["body"])){
return false;
}
$data=array($comment["name"],$comment["body"]);
$fp=fopen($this->log,"a");
fputcsv($fp,$data);
fclose($fp);
}
function testInvalidPush(){
$this->assertEquals($this->comments->push(array()),false);
}
Time: 0 seconds OK (4 tests)
余裕余裕。こんな感じでやっていけばいいのか。しかし絶対順番が逆だ。まずはテストを書くべきだな。
コメント機能が付きました。
スパム対策が適当だけど、突破されたらまたそのとき考えることにする。
]]>sudo apt-get install gtk-recordmydesktop
実際にはSynapticから入れたけど、それにしてもAPTは偉大だなあ。setup.exeとかsetup.msiとかがいかに面倒かよくわかる。
適当に範囲指定したあと「録音」ボタンを押す。終わったら停止。
ていうかなんで「録音」なんだ。せめて「録画」じゃないのか。他にも訳が怪しいところが多々あるけど、使用には問題ないからどうでもいいか。
特に思いつかなかったのでAutoPagerizeの動作を録画してみた。音は要らないのでキャプチャしないようにチェックボックスを外す。
動画の保存形式が.ogv(ogg video)なので、なんとなくYouTubeアップ前にflvに変換してみた。ついでにリサイズも。
ffmpeg -s 320x240 -i autopagerize.ogv -f flv autopagerize.flv
なぜか画面全体が薄い黄緑? かなんかで着色されてる。昔のモニタみたいだ。まあいいか。
何回か練習してまともなのが撮れたので、YouTubeのアカウント取ってアップロードした。だいたい5分くらいで見れるようになった。
ちょうど1分なのはたまたま。
]]>アジャイルプラクティス 達人プログラマに学ぶ現場開発者の習慣
タイトルにプログラマとか開発者とか入ってるけど、別にプログラミングに限らない気がするなあ。あとアジャイルにも限定されないと思う。
プロダクティビティ向上のために、アンチパターンや注意すべき点や成功体験などなどがいっぱい書いてあるのでこれは啓蒙書になるんだと思う。知識として持ってても、やらなきゃ無価値。やらない理由探しを抑止するため、最初に悪魔の囁きがエピグラフっぽく提示してある。
例えば「わかるまで質問する」っていうセクションだとこんな感じ。
言われたとおりにしてりゃいいんだ。「ここが問題なんだ」と言われたら、そこだけ見てればいい。幻影を追い求めても時間の無駄だ。
そしてこの行動がいかに間違っているかを説明し、最後に正しい道を歩んでいるときの「こんな気分」という短いセンテンスでまとめる。
まるで宝石を採掘しているかのような気分だ。無関係の物質をふるい落としながら深く深く掘り下げていった末、ついに光り輝く石を見つける。症状だけでなく、真の原因も突き止めていることを実感している。
蛇足的に「バランスが肝心」と題して、いくら正しくても盲目的にそのとおり振る舞っていては意味がないので意識的に取り組もうみたいなことが書いてあって1セクション終わり。
最初に書いたようにこれは啓蒙書なので、意固地な人とかが読んでも綺麗事を羅列したただのポエムにしか見えない。意識の高い人が読んで、さらにそれを実践してこそ良書たりうる。僕はそれほど意識が高くないので、悪魔の囁きが間違ってる理由の理解と、「こんな気分」の心境を妄想して済ませまくった。
あとちょっとしたエピソードとして、Rubyのインタプリタについて言及されてたのが面白かった。RubyインタプリタというのはRubyで書かれたスクリプトを解析してCだかマシン語だかに変換するプログラムです。
ロジックが適切な命名とともに整然と記述されていれば、ほとんどコメントはいらない。実際、アンディとデイヴ・トーマスが最初にRubyの書籍を執筆したときは、Rubyインタプリタのコードを読むだけで言語全体のドキュメントを書けた。これは、Rubyの生みの親(まつもとゆきひろ)がコメントに頼らず、そのコードで雄弁に物語ってくれていたおかげだ。なにせアンディとデイヴは二人とも「スキヤキ」と「サケ」以外の日本語なんて知らないんだから。
こういう実例もいろいろ散りばめられてる。主題の説得力が増強されつつ読み物として面白いのでやっぱり良書だなあ。まあ何はなくとも実践ですが、とりあえずテストから始めようかなと思った。
各ブラウザでのレンダリングも自動でテストできたらいいのに。やるとしたらスクリーンショット撮って画像の同一性をチェックするか、JavaScriptで各ノードのoffsetTopやらoffsetHeightをチェックするとかかなあ。
]]>マシンは2台あって、このブログをホストしてるサーバと、RedMineがましますデスクトップ。RedMine用のサブドメインをVirtualHostして、普通にmod_proxyでhttp://desktop:3000/へプロクシることにする。
sudo a2enmod proxyでproxy有効に。VirtualHostをだいたいこんな感じで書く。
<VirtualHost *> ServerName redmine.tt25.org CustomLog /var/log/apache2/r.log common RewriteEngine on RewriteRule ^/(.*)$ http://desktop:3000/$1 [P,QSA] </VirtualHost>
デスクトップからredmine.tt25.orgを見にいくと待望のエラー。500でも404でもなく403ってことはどっかでDeny from allしてるはず。RewriteRuleの[P]を外すと(リダイレクトになるものの)エラーは出ない。
いろいろAllow from allしてみるけど事態が一向に改善しないので、error.logのメッセージ「client denied by server configuration: proxy:」でググると即座に解決した。<Proxy *></Proxy>の存在を忘れてた。
そういやDebianはDocumentRoot以外の全ディレクトリもデフォルトでDeny from allされてたっけな。httpd.confに書いてもいいけどまた忘れそうだったので、/etc/apache2/mods-enabled/proxy.confのほうにAllow from allを追記してforce-reloadして見にいくといけた。
アクセス制限しなくてもいい気がしたけどやっとかないといけない気がしたのでダイジェスト認証することにした。sudo a2enmod auth_digestで有効に。せっかくいろいろ削って軽量化したApacheがどんどんごてごてしていくなあ。
# htdigest -c /path/to/redmine/.htdigest "redmine" tt Adding password for tt in realm redmine. New password: Re-type new password:
で.htdigestをredmineディレクトリに生成。次にApache側の設定。
<VirtualHost *>
ServerName redmine.tt25.org
# CustomLog /var/log/apache2/r.log common
RewriteEngine on
RewriteRule ^/(.*)$ http://desktop:3000/$1 [P,QSA]
<Proxy *>
AuthType Digest
AuthName "redmine"
AuthUserFile /path/to/redmine/.htdigest
Require valid-user
</Proxy>
</VirtualHost>
最初<Proxy>じゃなくて<Directory>に書いてしまってまったく機能しなかったけど、さっき思い出したばかりの<Proxy>にしてみたら期待通り動いた。ついでにもう用済みっぽい感じがするCustomLogもコメントアウトしておく。
できた。 そうか朱肉を買おうと思ってたんだった、というのが退社前にばっちりわかるようになりました。あと面倒だったので「雑務」とか「feature」とかのトラッカーは既存のものを流用した。だいたい意味は通じるから大丈夫だろう。
]]>
Civ4やりすぎて疲れてきたのでたまには違うゲーム。AoE3はたぶんまだ延べ20時間くらいしかやってないけど、そろそろCiv4の癖は抜けてAoE的な、あるいはRTS的なプレイに慣れてきた。
食料はいつも草に生えてる苺みたいなのを採る→粉ひき所建造→粉ひき所強化って順番で確保してて、家畜を飼ったり、カピバラを射殺してナイフでくっちゃくっちゃしたりは(近くに苺がない限り)しないんですが、カピバラ10匹で4000食料にもなるんだったらもうちょい活用したほうがいいのかなあと思った。
あと粉ひき所強化もそうだけど、採取促進系のアップグレードの効果がいまいち体感できてない。アップグレードより入植者増やした方が効率いいんだろうか。苺つむのと粉ひくのと動物狩るのとだとスピードが違うのかどうかもわからん。
]]>僕がカピバラって言いたいがためにこれ をでっち挙げた直後、『世界樹の迷宮II』日記 を読んで我が身を恥じたのでもうちょいまともな何かを書いてみようかと思いました。
Civ4は想像ですが無印200時間、WL50時間、BtS100時間ほどやってると思います。学生時間で言えば不眠不休で冬休み丸ごと潰したようなものです。想像なので何とでも言えます。
ごく初期はごく単純な「ゲームで世界史追うのって面白え」という白い衝動に突き動かされていましたが、そういった刺激に慣れてくるといよいよゲーム内ルールに意識が準拠していくことになります。斧兵とかライフル兵とかはただのアナロジーである、と開眼してからがゲームの始まりです。
世襲制のために宗教ルート研究するのめんどいからある程度は資源や建物で幸福を確保しようとか、速攻で音楽研究して芸術家に文化ボムさせないと自国が立地的にやばいなとか、未プレイヤーからしたら意味不明な繰り言が頭の中を占領しはじめます。これはCiv4に限らずゲーム全般の醍醐味といえますが、戦略ゲームであるCiv4の真骨頂もまたここにあります。
労働者をつくるか戦士をつくるかはたまたピラミッドを建造すべきかというミクロな判断の積み重ねがゲーム終了に向けて蓄積されていくのもゲームですが、いかにして自国を強化し他国を引きずり下ろすかという単純な問いに対して数多の戦略を立て、実践し、修正し、対応していくサイクルもまたゲームです。目先の判断と大局的な判断が同居し、時に対立したりする状況下で事態を適切に処理していかなければなりません。ゲームも人生も、人生ゲームのようにルーレットを回してコマを動かすだけの簡単なお仕事ではないのです。
毎ターン大小の問題が配給されてくるなかで意識が朦朧としてきて曖昧な判断を下し自国首都が陥落したり、自分の描いた計略どおりに他国を滅亡させたものの同盟国だと思っていた隣国が宣戦布告してきて大量に都市を奪われたり、宇宙開発勝利を捨てて文化勝利狙いに切り替えたらあっさり勝てたり、といった失敗や成功の体験をもとに徐々に上達していく感覚を何度も味わってこそゲームを最大限に楽しめるのだと思います。
カジュアルゲームを否定する気はありませんが、たまには取り憑かれたプレイもいいんじゃないでしょうか。ときどき無駄なことに時間をつぎ込まないと感受性が摩耗していってなんか怖いです。
]]>★-----sshfs(~/tera <- /foo/bar)----◇----smbmount(/foo/bar <- \\landisk\disk1)----◇ 自宅 実家Linux TeraStation
↑実家Linuxが実家TeraStationをsmbmountで/foo/barへマウントし、自宅の~/teraへsshfsを使って実家Linuxの/foo/barをさらにマウントしてる。最終的に、自宅の~/teraの中に実家TeraStationの中身がマウントされます。
どう見ても実家Linuxを中継する意味がわかりかねますが、TeraStationがSSHを持ってないから仕方ないのです。
sshfsとは関係ないですが、他にもたまに実家WindowsへsshポートフォワーディングしてVNCしたりもしています。
★-------------ssh(1234 <- 5900)-------------◇ 自宅 実家Windows ★----------VNC(localhost::1234)--------------◇ 自宅 実家Windows
ところが昨日から実家Linux機が死んでしまっていて、自宅—TeraStation間の接続経路がなくなってしまいました。実家Windowsを中継させようにも、「ネットワークドライブの割り当て」は期待通り動かず、実家Windows(のcygwin)からTeraStationをマウントする方法がわからなくて諦めてた。
そこで登場するのがVMWare。実家Windows上でVMWareを起動し、VMWare上のLinuxで実家TeraStationをマウントすることにしました。
★--------ssh(1234 <- 2222)----------◇--------ssh(2222 <- 22)---------◆ 自宅 実家Windows VMWare ★-----sshfs(localhost:1234)-----◇-----ssh-----◆----smbmount------◇ 自宅 実家Windows VMWare TeraStation
相当むちゃくちゃだと思いますが、これで一応実家TeraStation上のmp3を自宅で再生できています。今週末には実家帰ろう。
]]>だいたいタブを40ほど開いて2,3日ほったらかしていると徐々に重くなってくるのが解せない。Operaはそんな弱い子じゃないはずだ。
って書いたら 「そんな使い方してるやつ居ねえ」と言われたので調べた。調べたっていうか、前に似たような使い方をいろいろ見た気がするので発掘した、ってほうが正しい。あとタブ40個で2,3日、というのは僕の感覚からすると桁が足りない相当控えめな数値であることをここに補足しておきます。
とりあえずいくつか見つけたものをリンクしとく。 http://mala.tumblr.com/post/2995353 とか http://d.hatena.ne.jp/Sybian/20070829/p1 とか http://www.flickr.com/photos/yuiseki/2136919308/ とか http://kuruman.org/diary/2007/05/01/opera-originarity#usage-light とか。全部Operaがらみなのがあれですけど、傍証としては充分かなと。
僕が初期(0.9.xごろ)のFirefoxにそれほど興味を持たなかったのはOpera/Sleipnirほどの軽快さがなかったからで、機能には憧れたものの実用には耐えなかった。UIがXULというマークアップ言語で書かれてる仕様が永遠にボトルネックになりそうって感じたから、拡張機能はチェックするけどメインに据える気はまったくなかった。今もあんまり気乗りしないのはひとえに重さのせい。重いというのはあらゆるアプリケーションにとって絶対的かつ致命的に悪だ。Netscape 4.xをかたくなに使い続ける人を支持する気はないけど、気持ちはわからんでもない。
タブブラウザやMDIエディタが出たてのころは、リソースの無駄とか片付けながら使えとかいう批判がけっこうあった。今やタブがないブラウザなんてないし、メジャーなエディタでいってもTerapadくらい? コンピュータの処理能力向上によってデメリットが無視できるほどに小さくなったとき、IEユーザー以外はみんなタブを必須要綱として挙げはじめた。他の似たような批判に、ウェブページに画像を載せるなら可能な限り減色しろとか、fontタグの分重くなるからチャットで発言に色を付けるな、なんてのもあった。要するに、過渡的なリソース上の制約を楯にUIを批判するのは的外れっていう確認。
次に使い方、というかスタイルというか。タブブラウザの常識化に伴ってブラウジングが劇的に変わった。気になるページはとりあえず新規タブで開いておいて後で読む、LDR/fastladderでピン立ててoで一気に10個開く、というのもタブブラウザありきだ。IE6ユーザーから見たら奇人にしか見えないと思う。けど、IE6なんて捨てた人からしたら、IE6ユーザーのほうこそ蛮族に見えるはず。
別に僕が最先端とかいうつもりはまったくなくてですね、キーボードのテンキーを切断 とか(ノコギリで切断したまんま回路丸出しで使ってる、もっとインパクトある画像を見た覚えがあるけど見つからなかった)、真っ黒なデスクトップ とか、デイトレーダーのマルチディスプレイ環境なんかも、極端な例だけど、要するに、使い方は環境と用途次第でいくらでも変わるし、違う環境下の人の違う使い方は奇異に見えるって話。
あれ、最初は何を言いたかったんだっけか。ええととにかくですね、タブ40個でひいひいいうようなブラウザにブラウザを名乗る資格はないとかそういうことを言いたかったはずです。タブ3桁を余裕で開けるようになったら、次は目当てのタブを簡単に探すための方法を模索しないといけないし、やることはいっぱいあると思う。Firefoxは現時点だとまだタブ40個すらまともにマネジメントできないのでもっとがんばれ。Operaもがんばれ。Webkitもがんばれ。IEは潔く死ね。
]]>人間がどういうふうに死んでいったかを集めた本。図鑑といいつつ図はないのでグロ描写が気になる向きにも安心。1人の人生はだいたい1〜4ページくらいにまとめられてるのですらすら読めるし、適当なところからぱらぱら読める。
解説や品評といった手垢は一切なくただ事実を書いてるだけなので、想像や思考の余地が最大限残されてる。人生について本気出して考えたり、諸行無常の響きを聞いたり、美学を伴った死に様に感銘したりできる。(語弊のある表現だけど)誤読の自由が保証されてるのは、国語の授業的読書に縛られた人の意識を解放すると思う。
ごく私的な願望をいえば、この本は享年の若い順に並べられてるけど、時期/享年/場所が完全にランダムなほうが良かったなあ。
]]>J-CASTニュース : アマゾン「自分がほしい物リスト」 全部公開されて大騒動
今はもう見れないみたい。つまらん。自分のウィッシュリストの設定を見たら「特定の人に公開する(「友だちに知らせる」でEメールを送信した人にのみ公開されます)」になってた。
ウィッシュリストは使うどころか存在を忘れてた。とりあえず全部カゴに入れておいて、買うときに「いまは買わない」をチェックするといった使い方をしてるので6個しかなかった。しかもそのうち5個は日付が同じ。
3年前のデータをちゃんと持ってるAmazonはえらいなあ。いや普通か。いい機会なのでちょっと振り返ってみよう。
なんでラノベなのかわからなかったけど思い出した。ライトノベル4大奇書 でドグラマグラに位置付けられてたからだ。他に理由が思い当たらない。未読and未購入。いま買おうと思ったら在庫切れだった。
たしか実家にあったしもう読んだ。くらげ君が現代的な理系メガネだったのは覚えてるので、その筋の人は読んで悶えるといいと思います。その筋の本ではなくて、ジャンルでいえばミステリです。
なんでウィッシュリストに入ってるのかは不明。
買おうかどうか迷ってたはず。2005年5月といえばたぶん無職だったので、1000円以上する本を買うのは思い切りが必要だった。
Wayback Machineのストック を読んだりなんやかやしてて忘れてたみたい。いま買ってもいいかなあ。
あとWayback Machineにあるページが(2003年なのに)XML宣言つきXHTML 1.1で書かれてたのが印象的だった。W3C主義者だからじゃなくて、たぶんこの文書を永続化しようとした帰結だなきっとそうに違いないうんそう絶対そう、とか当時思った。実際そういう意気込みが感じられる。
今は浦沢直樹ってだけでなんとなく購買意欲がなくなる。2巻か3巻までは読んだ気がする。ロボットが死んだり殺されたりしてたのは覚えてる。
実家近くで買って読んだ。
舞城王太郎のキャッチコピーに「圧倒的文圧」ってのがありまして、その表現の妥当性はともかく特徴的な文体なのはたしかです。一文がやたら長いけど勢いで読める、登場人物が福井弁、ちょくちょく2chみたいなのが出てくる、擬音が独特(赤ちゃんの泣き声がフギャーイッヒとか)などなどが共通してる感じ。
阿修羅ガールはどんな内容だっけか。忘れた。
庵野秀明がいかに私生活においてもオタクで安野モヨコもオタクかという日記。今でいうととなりの801ちゃん になるのかなあ。
]]>ソース読んだけど、だいたい以下のような流れで処理してる。
2.のそのままというのは、未知のプロパティは無視すべきっていうCSSの仕様を上手く使っててなるほどとは思った。将来どうなるかとかブラウザのバグとか考えると微妙なところだけど、発想は好きだ。
あとええとグローバル汚染しすぎとかいろいろあるけど、際立ってたちが悪いのがバリデーション。適当すぎて有害。
function validate(element, styles, pseudoClass) {
var pattern = getStyleAttribute(styles, "validate", "");
var noticeText = getStyleAttribute(styles, "validate-notice-text", "");
var noticeClass = getStyleAttribute(styles, "validate-notice-class", "");
var noticeExecute = getStyleAttribute(styles, "validate-notice-execute", "");
var submitForm = true;
var formElement = getParentNodeByTagName(element, "form");
formElement.onsubmit = function() { executeValidation(); return false; };
function executeValidation() {
switch(pattern) {
case "not-empty" : pattern = "/.+/"; break;
case "alphabetic" : pattern = "/^[a-z]+$/i"; break;
case "numeric" : pattern = "/^[0-9]+$/"; break;
case "alphabetic-numeric" : pattern = "/^[0-9a-z]+$/i"; break;
case "filename" : pattern = "/^[0-9a-züäö_\.\-]+$/i"; break;
case "email" : pattern = "/^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i"; break;
case "url" : pattern = "/^http:[a-z._%-%/]+$/i"; break;
}
eval('pattern = new RegExp(' + pattern + ');');
if(trim(element.value).match(pattern)) formElement.submit(); else {
if(noticeText != "") alert(noticeText);
if(noticeClass != "") element.className = element.className + " " + noticeClass;
if(noticeExecute != "") eval(noticeExecute);
}
}
elementReady(element);
}
メールの/^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/はまだお茶目な序の口。
URLの/^http:[a-z._%-%/]+$/iはもうftpやhttpsを通す気すらないし、ファイル名の/^[0-9a-züäö_\.\-]+$/iとかどんだけ悪夢だ。英語圏に限っていってもスペースが含まれてるだけでアウト。そもそもファイル名のバリデーションって何の意味があるんだ。
ついでにいうと、そこはswitchじゃなくハッシュ使え。evalの必要ないし、evalなんかするくらいなら最初からRegExpオブジェクト返せ。addEvent定義してるのになんでonsubmitに関数入れてるんだ。var submitForm = true;の意味がまったくない。executeValidationとかわざわざ定義せずonsubmitにインラインで渡せ。getParentNodeByTagNameをそこでしか使わないならインラインで書いたほうがちょっと速い。最初の引数patternがnot-emptyとかのどれにもヒットしない変な値のときの挙動が未定義。バリデーション通ったときわざわざformElement.submit()とか書かずにinvalidなときにイベントキャンセルしろ。
といったことを盛り込むとこうなった。動かしてないからバグってる可能性高。あといろいろ言ったけどこれでもまだ全然ダメな気がする。
function validate(element, styles, pseudoClass) {
var pattern = getStyleAttribute(styles, "validate", "");
var noticeText = getStyleAttribute(styles, "validate-notice-text", "");
var noticeClass = getStyleAttribute(styles, "validate-notice-class", "");
var noticeExecute = getStyleAttribute(styles, "validate-notice-execute", "");
var formElement=(function(node){
return (!node.parentNode) ?
node // 元の挙動どおりだけどこんなんでいいのか? nullを返すか例外投げるべきだと思う。
: node.parentNode.nodeName.toUpperCase() == "FORM") ?
node.parentNode
: arguments.callee(node.parentNode);
})(element);
// これもformだと決め付けてるけどformじゃない可能性があるのでどうかと思う。
addEvent(formElement,"submit",function(e){
var e=e || window.event;
var tmp={
"not-empty": /^./
,"alphabetic": /^[a-z]+$/i
,"numeric": /^[0-9]+$/i
,"alphabetic-numeric": /^[a-z0-9]+$/i
,"filename": // 意図が不明なので省略
,"email": // メアドバリデーションの必要性を感じないので省略。
// ↑強いて書くなら/^[^@]+@[^@]+\.[a-z]{2,}$/くらい大雑把にしないと冤罪が発生して困ると思う。
,"url": // ググればいくらでもありそうだけど面倒なので省略
}
var pattern=tmp[pattern] || throw new Exception("invalid validate label("+pattern+")");
if(!trim(element.value).match(pattern)){
if(noticeClass) element.className+=" "+noticeClass; // addClassとかはないのか。別にいいけど。
if(noticeExecute) eval(noticeExecute);
if(noticeText) alert(noticeText);
// 細かいけど、こういう保険がないとなんか不安。
if(noticeClass || noticeExecute || noticeText || false === false) alert("form error!");
// IE判定とかしてきちんとしたいところだけど面倒なので手抜き。
try{
e.preventDefault();
}catch(ex){
e.returnValue=false;
e.cancelBubble=true;//うろおぼえ
}
}
});
elementReady(element);
}
どっかから拾ってきたスクリプトというのは常にこういう問題が潜んでるわけです。ある日、httpsのリンクを張ろうとしてエラー出てどうしようもなくなったり、IE7じゃ動かなかったけどもうスクリプトの更新止まってるしどうしようとか慌てたり、前任者から引き継いだもののMoreCSSなんて知らないので途方に暮れたりするわけで、盲目的にJavaScriptを使うのはリスキーな行為だと認識しないといけないと思います。
CSSの中にいろいろ書けて便利、というのはたしかにそうでしょうけど、リスクに見合うだけのものが得られるかどうかを見極めないといけないと思います。MoreCSSなんてマイナーなもの、知ってる人のほうが少ないわけで、何か困ってもトラブルシューティングはまったく期待できません。自力で解決できなかった場合、もし問題が起きたら以後ずっと起きっぱなしになるかMoreCSSの除去作業が発生するわけで、きれいなツールチップとかちょっとした効率化に見合うとはとても思えません。これはMoreCSSに限ったことではなくて、JavaScript全般に対していえることです。
冒頭にも書いたように、こんなの勉強するくらいならJavaScript自体を勉強したほうがはるかに有意義だと思います。
]]>とりあえずInfoLister の結果。わりと普通だと思う。
*** Extensions (enabled: 12, disabled: 2; total: 14) Adblock Plus 0.7.5.3 All-in-One Gestures 0.18.0 DOM Inspector 1.8.1.12 Firebug 1.05 Greasemonkey 0.7.20080121.0 InfoLister 0.9f.2 Live HTTP Headers 0.13.1 PrefBar 4.0.0 Relaxed the HTML Validator 0.9.2 Speed Dial 0.7.0.8 Tab Mix Plus 0.3.6 Talkback 2.0.0.12 Tree Style Tab 0.6.2008030904 [disabled] Vimperator 0.5.3 [disabled] *** Themes (15) ColorGnome Firefox (default) FormalGnome Jumbo GNOME Halloween iFox Graphite iFox Metal iFox Smooth Industrial Orbit Grey Custom Phoenity Aura Qute rein [selected] Tango Unofficial Tango *** Plugins Default Plugin
テーマがやたら入ってるのはいろいろ試した名残です。
Mouse Gestures じゃなくてAll-in-one Gestureなのは、右クリック+ホイールコロコロでタブを切り替えたかったから。
HTML Validator じゃなくて Relaxed the HTML Validator なのは、Linuxに対応してないっていわれたから。
Tree Style Tab はちょっと試してみたかっただけ。Vimperatorも同様。もうちょいFirefoxに慣れてきたら使う。
あとLastTab 入れようとしたら、Tab Mix Plusに統合されてるけどいいのか? ってきかれた。いつのまに。
タブを縦置きにしたいのでこのuserChrome.css をコピペ。
タブバーは左じゃなく右に表示させたいので、Tab Mix Plusオプションで表示>タブバー>タブバーの位置を「下部」に。
とりあえず御三家のみ入れた。
この3つはOperaにも 移植されてる ので、Firefoxの強みを生かすならもっといろいろ入れないとなあ。
この段階ならまだOperaでも再現できるなあ。もっと”Firefox”を使わないと。
]]>並み居る動画の中で
が一番よかった。Ultimate Utopia とかもそうだけど、外人は動画というと仲間が集まってショートムービー撮ることを指すみたい。楽しそう。
出てるのはフランス人か。道理でコスプレが堂に入ってるわけだ(特に根拠なし)。メインの人が女装か女の子か判然としないけど、いっぱいコスプレしてるので良いと思います。女装もコスプレの一種なので問題ないです。コスプレなんとかの店はコスプレに興味ない人が行くところです。
いや単純によくできてると思う。これに5以外のレーティングする人の意味がわからない。劇中でなんかのアニメ(のMAD的なもの)がオマージュで出てくるけど、ニコニコ動画には今後も蠱毒の壷として活躍し、生成されたよくわからないものを広めていってほしい。
]]>同じ生活をLinuxでもやろうとしたものの、どうもWin版に及ばない。NVIDIA公式ドライバを入れて、キャッシュファイルをtmpfs上に作らせ、GNOMEからXfceに乗り換えたら、操作の快適さこそWindows Operaに近づいたものの安定性は遠く及ばない。最新安定版の9.24も、最新不安定版のOpera 9.50 weekly buildも、Windows Opera 9.24ほど安定してない。特にFlash開いたときと、長時間起動しっぱなしのときによく死ぬ。だいたいタブを40ほど開いて2,3日ほったらかしていると徐々に重くなってくるのが解せない。Operaはそんな弱い子じゃないはずだ。
逆にFirefoxの戦闘力は見違えて上がった。せっかくなのでVimperator入れたり、Minibuffer+LDRize入れたりして変態的ブラウジングを束の間楽しんだ。この変態さにどっぷり浸かると旧来のブラウジングが遊びに思えるのかな。
タブ切り替えとかスクロールはOperaのほうが快適だけど、操作で見ればFirefox(+Vimperator)の圧勝かなあ。しばらくOperaと普通のFirefoxを併用してみることにする。
]]>外部にメールを送れなかったのは、外部にメールを送らないようMTAを設定していたからでした。これ以上ないほど単純明快な理由。
sudo dpkg-reconfigure exim4-configで、最初に出てきた設定のインターネットなんとかを選択して解決。たぶんこれが「ローカルのみ」になってたんだと思う。
Rubyで日本語メールを送信 を参考に引数とかをちょっと改造した。TMailはgem化されてるのでsudo gem install tmailで入れる。
require "rubygems"
require "tmail"
require "net/smtp"
require "kconv"
class MyTMail
def MyTMail.send (mail,host,mime="text/plain")
m=TMail::Mail.new
m.to=mail[:to]
m.from=mail[:from]
tmp=Kconv.tojis(mail[:subject]).split(//,1).pack("m").chomp
m.subject="=?ISO-2022-JP?B?"+tmp.gsub("\n","")+"?="
m.body=Kconv.tojis(mail[:body]||"")
m.date=Time.now
m.mime_version="1.0"
mime=mime.split("/")
m.set_content_type mime.shift,mime.shift,{"charset"=>"ISO-2022-JP"}
m.write_back
Net::SMTP.start(host||"localhost"){|smtp|
smtp.sendmail(m.encoded,m.from,m.to)
}
end
end
#MyTMail.send({
# :to=>"mymail@example.com",
# :subject=>"aaてstてst",
# :from=>"foo@localhost",
# :body=> <<BODY
#てすと
#てすとp
#BODY
#},nil,nil)
最初TMailなのにTmailと書いてて1時間くらいハマったのはひみつ。だってエラーメッセージがuninitialized constant MyTMail::Tmail (NameError) とか`send’: {:subject=>”test”,:to=>”foo@example.com”} is not a symbol (TypeError)なんて言うからー、どっかでスコープが狂ってるのかなー、引数おかしかったっけなー、なんかしたっけなーってググってたからー。
わざわざクラスにしてるのは、最近僕がグローバル空間に関数定義するのイヤイヤ病を患ってるからです。MyTMailが定義されるんだから同じことなのにね。
しかしそれにしてもRubyぽくない書き方だ。せめてdef TMail::Mail.send_ja mail,server=”localhost”,mime=”text/plain”じゃないのか。出来るか知らないけど雰囲気的に。
あとmy.*はサンプルコードでのみ許されるものであって実際に使うなバーカって誰かが言ってた気がする。全然ダメだ。
グローバルな領域にbindingって変数があるみたい。p bindingで確認したらBindingオブジェクトとのこと。この説明 じゃbindingはメタ変数というかfooとかhogeの類だと思うじゃないですか。違うんですよ。
というわけで、
require "erb"
erb=ERB.new <<TEMPLATE
<p><%= i[:num] %></p>
TEMPLATE
dummy=[{:num=>1},{:num=>2}]
dummy.each{|i|
erb.run(binding)
}
でめでたく目的を達成できましたとさ。
ちなみに、erb.runはputs erb.resultと同じようなもんみたい。puts erb.runすると末尾に毎回nilが出るのが不思議だったけどそういうことか。
DateとDateTimeはrequire “date”しないと使えない。
require "date"
puts DateTime.now.strftime('%Y-%m-%d %H:%M:%S') #=> 2008-03-10 00:12:23
strftimeは万国共通のはずなのでそんなに迷うこともなかった。問題はnowをnewだと2分くらい思い込んでたことだ。
Amazon::ItemSearchをリファクタリングしようとした僕は、いつしかXMLツリーが生い茂るREXMLのジャングルへと迷い込む。XPathの道標はあらぬ方向を指し示し、もはやGoogleの光も届かない。疲弊と苛立ちの極限状況下で垣間見た異形のスクリプトとは? この日、Amazon河流域で僕はRubyを発見する。
まだ何もしてないけどだいたいそうなるはずなので乞うご期待。
]]>1.の可能性はおそらくない。トップページ をざっと見たけど、なんというか、実に外人だ。Ruby=Made in Japan && ゴーストって日本語でなんだ?=obake!とかまったく考えそうにない。
2.の可能性を潰すためにobake.gifでイメージ検索してみる。162件全部見たけど一致するものはなし。念のためWeb検索で「obake filetype:gif」「inurl:obake.gif」で検索してみるけどヒットなし。ていうかTextile Referenceに実在してるんだから理想的にはヒットしてしかるべきなのにヒットなし。まあいいや。もし見つかったら次はどんなクエリで検索したのか考えないといけなかったけど、その必要もなかったようでひとあんしん。
あやふやな消去法によって、可能性は3.のその他に託された。
とりあえずobakeでググってみると英語版Wikipedia が見つかった。あれ英語化されてたのか? と思って中を読んでみても、単に「日本のモンスターや霊魂のことだよ」「だいたい本性は動物だよ」って説明してるだけで、一部で流行ってる雰囲気すらない。だいたいobake.gifはどう見てもお化けっていうよりゴーストっぽい。戯画化が進んでるところは日本ぽいけど、ドット絵だしなあ。
obakeでググっても日本語サイトばっかり引っかかるので、URLに&hl=enを付けて英語版Googleを見ることにした。すると日本語版には居なかった怖い感じの人が出てきた。 さっきイメージ検索したときはこんなの見なかった。そのまま画像検索結果へ飛ぶとabout 13,000 for obake。なんで日本語版の100倍もヒットするのか意味不明ですけどまあいいや。Googleのイメージ検索のショボさがまたひとつ浮き彫りになっただけだ。
なんで丑三つ時にお化け画像を延々と見てるのか疑問に思いながら次へ次へ。たまごっち の画像がちょっと似てると思ったけど、比べてみたら そんなに似てない。けど、たしかに元のobake.gifはたまごっちっぽいタッチだなと思った。
貞子が2回も出てきたけどそれはお化けじゃないだろうと思いながら次へ次へ。お化けじゃないならなんだろう。妖怪でもないし怨念とかでもないし。間違いない答えは超能力者だろうけど、あんまり適切な呼称でもないよなあ。じゃあ、とか考えてたら8ページ目でついに発見。 サイト下方にある画像はファイル名もばっちりobake.gifで、ファイルサイズやフレーム数も一致する。経緯は知らないけどここから持ってきた画像なのは間違いなさそう。
ちなみにこのサイト はたまごっち墓地ってサイトで、自分のたまごっちが死んだらここに投稿するらしい。書き込んでるのはたぶん子供だろうけど、けっこう悲しんでるみたいなのはなんとなくわかった。I loved you so much Daniel!!!とか、実際に墓の前で叫んでたらもらい泣きする人多数だろうな。
というわけで、他にも「このサイトの作者はなぜobake.gifなんて付けたのか」「Textileの人はどういう経緯でこのサイトに来たのか」「タイトルのcemeteryの綴りが間違ってたりトップの画像やURLだとGraveyardだったりするのはなぜか」「貞子はカテゴリ的にどこに属するのか」「なんで丑三つ時に英語のお悔やみの言葉を読みふけってるのか」などなど謎は尽きない。
]]>
秒速10万行でログを解析してくれるらしい。インストールはapt-get install visitorsしただけ。
visitors -A /var/log/apache2/access_log > result.htmlで上手くいくのを確認したので、シェルスクリプト書いてcronに入れておいた。
#!/bin/sh file="/path/to/save/`date +'%Y%m%d'`.html" cmd="visitors -A /var/log/apache2/access_log > "$file eval $cmd chown nobody $file
たぶん普通はワンライナーというかcrontabに直接書くんだと思うけど初心者なので仕方ない。eval $cmdもなんか不穏な気配がする。そのうち出来るようになろう。
あと、このブログは管理画面とかないので探しても無駄です。IE5が血迷ったかのように比率高いのは、YahooFeedSeekerがMSIE 5.5を自称してるせいです。
]]>
yotubato:
Title : "よつばと!"
filter: "comic"
sigurui:
Title: "シグルイ"
filter: "comic"
こんな感じで検索対象をYAMLで書いてRubyに渡してAmazon Web Serviceから情報取ってくるまでは出来た。問題は、
といったところ。実用というよりRubyの練習なのでPlaggerとかは使いません。
とりあえず腑に落ちてないところを列挙してみる。
require "erb"
erb=ERB.new <<ERB
title: <%= title %> <- error
title: <%= item["title"] %> <- error
ERB
items=[{"title"=>"foo"},{"title"=>"bar"}]
items.each{|item|
puts erb.run(item) # error
}
とかやると、itemを起点にしてtitle: fooが出てくると思ってたけどどうも違う。ERB#runの引数bindingの意味がいまいちわからない。
Module Amazon
BASE_URI ="http://ecs.amazonaws.jp/onca/xml?"
Class ItemSearch
def meth
puts BASE_URI # error
puts Amazon::BASE_URI # ok
puts self::BASE_URI # error(selfはItemSearchで、Amazonを継承してるわけでもないので未定義)
puts super::BASE_URI # error(superがない)
include Amazon
puts BASE_URI # ok
end
end
end
Moduleは抽象クラス兼ネームスペースって考えといたらいいのかな。
hash={:foo=>"bar",:hoge=>"fuga"}
case obj.class
when Symbol.class, String.class
hash[obj.to_sym] || {}
when Hash.class
obj
end
的なことをしたときにハマった。
hash={:foo=>"bar",:hoge=>"fuga"}
case obj.class.to_s
when "Symbol", "String"
hash[obj.to_sym] || {}
when "Hash"
obj
end
みたいにしたけど、明らかにダサいのでなんかやり方がありそう。
ng={
"foo"=>"foo"
,"bar"=>"bar" # ここでエラー
}
ok={
"foo"=>"foo",
"bar"=>"bar",
}
Klass.new
.method # ここでエラー
.method
finish
# これだといける
Klass.new.
method.
method.
finish
行末にあると視界に入らなくて不安になるので行頭に書く癖がある。手癖だけの問題ならまだしも、不安を伴ってるのでなかなか矯正できなくて困る。
]]>私たちは爆発する飛翔体の魅力にとりつかれている。
らしい。僕を含めた一部の人が、ドラクエで好きな呪文を訊かれてメラゾーマを挙げる理由がここに解明された。ルーラとかアバカムとか言わずただただ相手を焼き尽くすロマンチスト。見方によってはサディストとも言う。
主に軍事的な観点から投擲能力と放火能力の進歩を追っていく内容。殴る蹴る噛むなどの近接攻撃ではなく、離れたところから石を投げたり弓を撃ったりするのは動物的に前代未聞の攻撃方法だった。その優位を生かしてアフリカの外にも進出していった結果、中ボス的な大きいサイズの動物が世界のいろんなところで同時多発的に絶滅し、人類はその弱そうな体躯と裏腹に自然界を支配していく。
中ボスをあらかた片付けるとラスボスたる人類との戦いが始まる。石をより効果的に投げるための道具を考案したアステカ文明が圧倒的な軍事力を誇ったり、城壁を破壊するために巨大な投石機で岩をガンガン投げたり、動物の肉を火で焼くとおいしいことを発見したりして、そのうち道教の人が火薬を発見し、火力を使って投擲する武器であるところの火槍が生まれ、強化されて大砲になり、マスケット銃だのライフル銃だのがヨーロッパあたりで配備され、新大陸に居た先住民が銃よりも強力かつ正確な投石の能力を持っていて驚いたり、ナチスが爆撃機や戦闘機を空に飛ばしながらミサイル研究に血道をあげつつも崩壊したのち、最強兵器の水爆が完成し、人類は月を歩いて人工衛星を飛ばす。
物を投げて、離れたところに時間差で影響を及ぼすってところを著者が3回くらい強調してる。この能力(というか知覚)がなければアクションとリアクションは常にほぼ同時に起こるしかないわけで、「投資」とか「投げる」の字がついた概念は絶対に理解できないからなんとかかんとか。いわれてみれば、要望を投げるとか、視線を投げるとか、そういう非同期な要素を含んだ語彙は案外多い。
他にも、人類の普遍的な娯楽として花火を挙げている。あれは火薬の詰まった玉を火力で上空に「投げ」、「爆発させる」という一連の流れがエンターテインメントたり得るからだとかなんとか。
というような「投げる」「燃やす」という視点で歴史をつづっている本です。あとはこの本が投げ捨てられて焚書なればささやかなオチがついていいんじゃないかなと思う次第。
]]>デビルカッターは岩くだく、とかサビで延々と必殺技が紹介されるあのデビルマンをマンガで読んだ。読後、例の歌が能天気過ぎて撲殺したくなるほどマンガ版は重厚な内容だった。
第1話こそ学園ラブコメみたいに主人公と幼なじみがイチャイチャしてるものの、悪魔の存在が早々に披露され、主人公が悪魔と合体しデビルマンになってからは一転。
悪魔側の偉いのが相当な策士で、人類全体を国家レベルで混乱させ疑心暗鬼にさせる。普通に悪魔vs人類で戦争するよりは人類の自滅を狙ったほうが効率いいから。悪役がちゃんと賢いのは高ポイント。
結果、完全にバグった人類はといえば、自衛隊による魔女狩りが始まっておばちゃんが拷問器具で惨殺されたり、民間でも近所の人たちがたいまつ片手に家を襲撃し幼稚園児の首を包丁でぶった切って生首もってニヤニヤしたり、女子高生の四肢を引きちぎって棒に刺して宴会したりする。殺されたり踊ったりしてる人々はみんな、第1話では普通に生活してた人たちばっかり。人民の人民による人民のための地獄絵図。
主人公のデビルマンは悪魔たちと戦うため、中途半端に悪魔と合体して困ってる民間人の半デビルマンを集めて組織化するけど、上述のように人類がキチガイすぎるのを次々と目撃するにつれ、戦うべきは悪魔じゃなくて人類とか言い出して、いろいろあって、最終決戦ののちにマンガは終わる。
普通の人がキチガイ化するとこはなんとなく漂流教室を思い出したけど、あっちは閉じた世界での出来事なのに対して、デビルマンはごくオープンな日常生活での一コマ。のび太が恐竜と戦ってもなんとでもなりそうだけど、サザエさんのイササカ先生がアルツハイマー発症してボケたら、それはもうサスペンスだよな、みたいな。
たぶん子供が読んだらパニクりながら号泣して頼むから今晩一緒に寝てくれって訴える。なので、たとえ一夜でも子供と添い寝したい人にはオススメの5冊。自分で書いといてなんだけど、この濃度でたった5冊てのがいまいちピンと来ない。
]]># ruby -v ruby 1.8.6 (2007-09-24 patchlevel 111) [i486-linux] # /usr/sbin/ab -c 25 -n 500 http://localhost:3000/ This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0 Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Copyright 2006 The Apache Software Foundation, http://www.apache.org/ ...
# thin --version thin 0.7.0 codename Spherical Cow # thin start -d -e production
Server Software: thin
Server Hostname: localhost
Server Port: 3000
Document Path: /
Document Length: 3183 bytes
Concurrency Level: 25
Time taken for tests: 8.948900 seconds
Complete requests: 500
Failed requests: 0
Write errors: 0
Total transferred: 1753000 bytes
HTML transferred: 1591500 bytes
Requests per second: 55.87 [#/sec] (mean)
Time per request: 447.445 [ms] (mean)
Time per request: 17.898 [ms] (mean, across all concurrent requests)
Transfer rate: 191.20 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 1
Processing: 305 443 150.0 423 1292
Waiting: 192 303 125.8 286 1080
Total: 305 443 150.1 423 1292
Percentage of the requests served within a certain time (ms)
50% 423
66% 439
75% 453
80% 478
90% 517
95% 742
98% 1079
99% 1292
100% 1292 (longest request)
# mongrel_rails start --version Version 1.1.4 # mongrel_rails start -d -e production
Server Software: Mongrel
Server Hostname: localhost
Server Port: 3000
Document Path: /
Document Length: 3183 bytes
Concurrency Level: 25
Time taken for tests: 14.808434 seconds
Complete requests: 500
Failed requests: 0
Write errors: 0
Total transferred: 1769500 bytes
HTML transferred: 1591500 bytes
Requests per second: 33.76 [#/sec] (mean)
Time per request: 740.422 [ms] (mean)
Time per request: 29.617 [ms] (mean, across all concurrent requests)
Transfer rate: 116.69 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 1
Processing: 358 728 666.2 483 3413
Waiting: 358 728 666.2 483 3413
Total: 358 728 666.3 483 3413
Percentage of the requests served within a certain time (ms)
50% 483
66% 501
75% 556
80% 581
90% 1580
95% 2704
98% 3241
99% 3333
100% 3413 (longest request)
# ruby script/server webrick -e production -d
Server Software: WEBrick/1.3.1
Server Hostname: localhost
Server Port: 3000
Document Path: /
Document Length: 3183 bytes
Concurrency Level: 25
Time taken for tests: 19.188079 seconds
Complete requests: 500
Failed requests: 0
Write errors: 0
Total transferred: 1774000 bytes
HTML transferred: 1591500 bytes
Requests per second: 26.06 [#/sec] (mean)
Time per request: 959.404 [ms] (mean)
Time per request: 38.376 [ms] (mean, across all concurrent requests)
Transfer rate: 90.26 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 2
Processing: 120 926 584.8 656 3060
Waiting: 118 923 585.1 652 3053
Total: 120 926 584.8 656 3060
Percentage of the requests served within a certain time (ms)
50% 656
66% 716
75% 754
80% 1517
90% 2064
95% 2196
98% 2288
99% 2324
100% 3060 (longest request)
僕が普段使ってる環境で動かすならMongrelよりthinのほうがお得。WEBrickは遅いけど、数ヶ月前に見た噂によると勉強には適したソースコードだとか。
ていうかそういう意図なら-c 25である意味はまったくなかったなあ。まあいいか。
]]>当然ながらrubyとかsubversionとかrubygemsは予めインストールしておかないといけない。既に入ってたので省略。
# sudo gem install mongrel sqlite3-ruby RedCloth -y # svn co http://redmine.rubyforge.org/svn/branches/0.6-stable redmine-0.6 # cd redmine-0.6
必要ファイルは揃った。他にもrakeとか必要かも知れないけど省略。しかし手軽すぎる。
続いて設定。config/database.yml.exampleをconfig/database.ymlにコピーして、そいつを編集する。SQLite3なのでユーザーとかはてきとう。
production: adapter: sqlite3 database: redmine_data.sqlite3.db host: localhost username: root password:
んでmigrate。DB作ったりとか、要するにイニシャライズ。
redmine-0.6# rake db:migrate RAILS_ENV=production
(中略)
-- create_table("custom_fields_projects", {:id=>false, :force=>true})
-> 0.1168s
(中略)
redmine-0.6# mongrel_rails start -e production ** Starting Mongrel listening at 0.0.0.0:3000 ** Starting Rails with production environment... ** Rails loaded. ** Loading any Rails specific GemPlugins ** Signals ready. TERM => stop. USR2 => restart. INT => stop (no restart). ** Rails signals registered. HUP => reload (without restart). It might not work well. ** Mongrel 1.1.3 available at 0.0.0.0:3000 ** Use CTRL-C to stop.
http://localhost:3000/を見るとちゃんとRedMineのトップページが表示されたのでCtrl+Cでいったん終了。改めてmongrel_rails start -e production -dしてデーモンにする。
ローカルで使うだけなので、Apache2.2+mod_proxyとかの設定はしない。
約15分。rake migrateが通らなくて困った。rake db:migrateが正しいみたい。
]]>sqlite3がどうのこうのとエラー。
sudo apt-get install sqlite3-dev sqlite3-ruby sudo gem install sqlite3-ruby
入った。
require "rubygems"
require "active_record"
ActiveRecord::Base.establish_connection(
:adapter=>"sqlite3",:dbfile=>"amazon.db",:database=>"amazon.db"
)
InitialSchema < ActiveRecord {
def self.up
create_table :items{|t|
t.column :url,:string
t.column :asin,:string
t.column :title,:string
}
end
}
InitialSchema.migrate(:up)
エラー。
# cat s.sql CREATE TABLE items ( id INTEGER PRIMARY KEY NOT NULL ,title VARCHAR NOT NULL ,asin VARCHAR NOT NULL -- CHARの桁数数えるのめんどかった ,url VARCHAR NOT NULL ); # sqlite3 -init s.sql amazon.db sqlite> [Ctrl+D] #
ここまで来るのに2時間くらい掛かって嫌になってやめた。
]]>サーバはML115 にメモリ3GB積んで、Debian etch AMD64版いれてる。
]]>
なぜか同じ本が2冊入ってた。
]]>