2016年Advent Calendarまとめ

2016年12月25日
区分
advent-calender
報告者:
chiba

こんにちはchibaです!

本来のAdvent Calendarは24日までで、25日は粛々と迎えるということですが、日本のプログラミング界隈を起源とするものではなぜか25日まで実行するのが習わしとなっています。

ということでネタも切れてしまったので、今年の当社のAdvent Calendarを振り返ってみることにしました。

今年も昨年と同じくQiitaを利用させて頂きましたが、参加メンバーは11名から7名へ減少、デザイナー、ディレクター、エンジニアが参加していたのが、今年はエンジニアのみでiOS/Android系の記事が大半となりました。
当社は今年はiOS/Androidエンジニアが元気です。

こういう業務とは微妙に関係しているようなしていないようなものは、忙殺されていたり、心に余裕がなければなかなか参加したくもなりませんので、皆でわいわいやれるのが、チームの健康のバロメータにもなる気もします。
来年開催するとしたら、デザイナー位は巻き込んでいけたら良いですね。

ということでまた来年!
ハッピーハッキング!

当社のOSS活動

2016年12月24日
区分
advent-calender
報告者:
chiba

こんにちはchibaです! 今回は、当社のOSS開発支援制度について書こうと思います。

当社はいこーよを始めとして、Rails等、自由ソフト/OSSを用いて開発されています。
OSSの恩恵に浴するからには、是非とも御返しもしたいところですが、開発者個人の活動はあるものの会社としての取り組みはありませんでした。

そんな当社でしたが、今年の10月から月10時間まで会社の取り組みとしてOSS活動を支援してくれることになりました。やった! 貢献するOSSプロジェクトに関しては特に制限はありません。

とりあえずお試しで10時間ということでスタートしたのですが、これまで開発していたものが10月から急にOSSにできるわけもなく、また、業務時間内となると直近のタスクを優先してしまいがちになるので、開発者は今のところあまり活用できていないようです。

そんな私も、自分のプロジェクトに来て放置していたプルリクを片付けたり、折を見付けては、ちょっとした修正のプルリクを数点投げるなどで大して活用できていません。

折角の良い取り組みだと思うので活用して行きたい!!
まずは活用できる仕組み作りからかもしれないですね。来年はどうにか工夫して活用しようと思います。

サーバー謎の再起動サーガ

2016年12月17日
区分
advent-calender
報告者:
chiba

こんにちは!、chibaです!
この記事はアクトインディ Advent Calendar 2016 17日目です。

今回は、新規に導入したサーバーの謎のハングアップの原因を追い掛けた話です。

昨年度末に弊社のdockerが急増していることもあり、メモリ容量が大きく速いマシンを導入しようということになりました。
メモリ容量が最重要ポイントだったので、天下のドスパラさんの128GiBメモリマシン:raytrek 128を購入。
docker環境もセットアップして稼動を開始しました。

謎の再起動発覚

セットアップ当初は色々環境を整えるので、連続稼動することもなく気付くこともなかったのですが、急にマシンが再起動していることに気付きました。
マシンは共用の実験機として使われていたので、誰かが再起動しちゃったのかなあ、位で流していましたが、どうも監視してみると1週間位すると再起動してしまう様子。
面倒ですが、ハードウェアのサポートに電話してチェック方法を伺い、問題あれば部品の交換等をしてもらおうかなと思っていました。
しかし、メモリチェックを複数のツールで実行しても問題なし。
メーカーに検品に出すにも構築したデータを退避しなくちゃいけないし、面倒だなあああ、とぐるぐる悩みつつ、しばらく経過してしまいました。

ハングアップ画面を捉える

それまで勝手なタイミングで再起動していたのですが、ログには何もでてこない状況でした。
カーネルをアップグレードしたりなんやりしていたら、現象がちょっと変って今度はハングするようになってくれました。
しかし、ハングのメッセージが取得できただけ前進です! ログには下記のようなメッセージが出ていました。

NMI watchdog: BUG: soft lockup - CPU#0 stuck for 22s! [kerneloops:...

CPUが刺さっているようですが、とりあえずググるとこんな議論がされているのを発見しました。

全体的に謎なままですが、どうもCore-i7とnVidiaのグラボと特定のLinuxカーネルで起きているようです。
こんなことあるのでしょうか。
ものは試しということで、安いATIのグラボに差し換えてみたら、安定して稼動するようになりました。

個人的にもLinuxは17年位デスクトップ機としても使っていますが、こんなことは初めてです。
結局あまりすっきりしない結末でしたが、こんなこともあるんだなあ。
しかし、こういった謎の組み合わせ問題にはもう遭遇したくないものです。

最強のインフラ用スクリプト言語を探す旅にでます(其の二)

2016年12月10日
区分
advent-calender
報告者:
chiba

こんにちは!、chibaです!
この記事はアクトインディ Advent Calendar 2016 10日目です。

さて前回最強のスクリプティング言語を導入しましたが、早速スクリプトを書いてみましょう。
今回お題とするのは、dfの出力をシンプルにし、カラムを好きな順番に並べ変えたものです。

これまでのUnixシェルスクリプトでは、こんな感じに書いていました。

#!/bin/sh

df -lh -x tmpfs |awk '{printf("%2d%%%6s %s\n",$(NF-1),$(NF-2),$NF)}'|tail -n +2|sort -nr

一行野郎がそのままスクリプトになってしまった感じですね。

出力はこんな感じです。

69%   30G /foo
58%  7.5G /
53%   46G /bar
48%  271G /baz
39%  1.6T /quux
33%  150G /zop
26%  650G /
10%   85G /zot
 1%  7.7G /dev

ディスクの空き容量を適当に観察したいなと思って作りました。
awkや、tailsortのコマンドの知識が要求されるのが辛いですね。
1970-80年代の知識を蓄えるのつらい……。

さてこれを輝ける未来のPerl6で書いてみました。

#!/usr/bin/env perl6

my @df = qx/df -lh -x tmpfs/.lines[1..Inf];
my @ans = @df.map({ my ($,$,$,$left,$pct,$dir) = .words;
                    [$pct.chop.Int,$left,$dir] })
             .sort({$^a[0] < $^b[0]});
for @ans -> ($pct,$left,$dir) {
  printf("%3d%% %4s %s\n",$pct,$left,$dir);
}

$や、<^等尖った記号が多くて若干茨のような見た目になっていますね。
もっと短くは書けると思いますが、圧縮して書いても良いことなさそうなのでやめました。

解説をすると、q:x//(qxとも書ける)でコマンドを実行した結果を取得できるので、その結果をlinesで分解し、2行目以降を配列とします。
次にmapでそれを加工し、sortでソートします。
ソートは数値で行う必要があるので、chopで%を取って.Intで整数に変換しています。
そして最後にforで配列を回してprintfで整形して表示する、といった具合です。

自分は、バックグラウンドがLispなものでmapしがちなのですが、そんなmap野郎でもお手軽に扱えて嬉しいです。

そんな訳で、来年は未来の言語(Perl6)を活用していきたいです!

最強のインフラ用スクリプト言語を探す旅にでます(其の一)

2016年12月05日
区分
advent-calender
報告者:
chiba

こんにちは!、chibaです!
この記事はアクトインディ Advent Calendar 2016 5日目です。

インフラまわりでシェルスクリプトを書いたりする日々ですが、このままシェルスクリプトを書いていて良いのだろうかという思いがつのっています。
もちろんシェルスクリプトは便利ですし、Unix作業の殆どのことはさらっと書けます。

しかし他のプログラミング言語のようにちょっと難しいことをやろうとした途端、ややこしいことになる印象が強いです。

弊社は、現在Rubyが主力言語ですので、Rubyに統一しても良いのかなとは思いますが、最強のスクリプティング言語を探す旅に出ることにしました。 そして、旅に出かけるドアを開けた直後に出会ったのがPerl6です。

昨年、ついに正式リリースとなったPerl6ですが、昨年は正式リリース前に書いていたスクリプトが結構動かなくなったりしていたので、何かを書くにはちょっと時期尚早かなあと思っていたのですが、もう1年経ったので多分大丈夫なことでしょう。

まずはダウンロード

まずは、Perl6自体を導入するところからです。
昨年ちょっといじったところでは、Rakudoの実装と、Pandaというパッケージマネージャーを使うというのが定番のようでした。
今現在でもこの組み合わせが定番のようですので、これを導入します。

導入手順はこちらに記載された通りに行いました。

https://perl6.org/downloads/

非常に親切ですが、 perl Configure.pl --gen-moar --prefix /opt/rakudo-star-2016.11 の箇所はルート権限が必要だったりするのに何度か引っ掛かりました。

とりあえず、ビルドが終了しmake installせよとなるので、実行します。

インストール終了のメッセージの最後には、

/opt/rakudo-star-2016.11/bin
/opt/rakudo-star-2016.11/share/perl6/site/bin

をパスに追加しておいてね、と書いてあるので、これに従います。

今回は、ちょっと奥ゆかしくパスの後ろに追加しておきましょう。

export PATH=$PATH:/opt/rakudo-star-2016.11/bin:/opt/rakudo-star-2016.11/share/perl6/site/bin

さて、perl6コマンドでperl6を起動です。

> say 8 x 4
8888

これで最強のスクリプト言語を書く準備ができました! 〜続く〜

Postfixのaliasesのdiffを見易くする

2016年08月12日
区分
システム管理
報告者:
chiba

こんにちはchibaです!
弊社では、メールサーバー(Postfix)のaliasesもgitで管理していたりするのですが、マージのレビュー等でdiffを確認するのが億劫でした。

ご存知の通り、aliasesは、

foo: a@example.com, b@example.com, c@example.com, z@example.com, q@example.com
bar: a@example.com, b@example.com, c@example.com, q@example.com
baz: a@example.com, b@example.com

のような形式なので、変更箇所を目視確認するのがとても面倒なのです。

そんな日々でしたが、ふとman aliasesしてaliasesの形式の確認をしてみたところ、

·An alias definition has the form

     name: value1, value2, ...

  ·Empty lines and whitespace-only lines are ignored, as are lines whose first non-whitespace character is a `#'.
  ·A logical line starts with non-whitespace text. A line that starts with whitespace continues a logical line.

なんてことが書いてありました。空白から始まっていれば、先行する行から続いて一行の論理行と見做されるとな!

さっそく上記の形式を、

foo:
        a@example.com
       ,b@example.com
       ,c@example.com
       ,z@example.com
       ,q@example.com

bar: 
        a@example.com
       ,b@example.com
       ,c@example.com
       ,q@example.com

baz: 
        a@example.com
       ,b@example.com

のようなものに変更してみました。

これでaliasesの変更点がはっきりするようになり幸せになりました。

端末が壊れても安心! ed & ex の最低限の使い方

2015年12月24日
区分
AdventCalender
報告者:
chiba

アクトインディ Advent Calendar 2015の24日目の記事です。

こんにちは、chibaです。 色々なサーバーで作業していると稀に端末が壊れてしまった状況に遭遇することがあります。
こういう場合、大抵は端末を直している暇はなかったりしますが、そんな場合でも安心して編集できるのはラインエディタです。
今回は、そんな非常時でも安心して利用できるed(と互換性の高いex)の最低限のコマンドを紹介したいと思います。

ファイルの中身を表示する

まず、

0 1 2 3 4 5 6 7 8 9 0 1 1 2 3 4 5 6 7 8 9 0 2 1 2 3 4 5 6 7 8 9 0 というファイルfoo.txtがあったとします。

$ ed foo.txt で起動します。

54 %p 0 1 2 3 4 5 6 7 8 9 0 1 1 2 3 4 5 6 7 8 9 0 2 1 2 3 4 5 6 7 8 9 0

%pでファイル全体を表示します。
大体は、viのコマンドと同じですので、viを使っている人は簡単に操作できると思います。

ファイルの保存と終了

編集内容の保存は、wで、終了はqです。
わかりやすいですね。

ファイルの中身を編集する

行の後ろへの追加

ここで、最終行に行を追加してみたいと思います。

%a 4 1 2 3 4 5 6 7 8 9 0 .

%aで最終行(%)に(A)ppendしています。
編集の終了は、改行して単独の.を入力します。
こんな感じになりました。 %p 0 1 2 3 4 5 6 7 8 9 0 1 1 2 3 4 5 6 7 8 9 0 2 1 2 3 4 5 6 7 8 9 0 4 1 2 3 4 5 6 7 8 9 0

行の前への挿入

4行目の前に3 1 2 3 4 5 6 7 8 9 0を追加したいとします。
その場合下記のように書けます。

4i 3 1 2 3 4 5 6 7 8 9 0 .

こんな感じになりました。 :%p 0 1 2 3 4 5 6 7 8 9 0 1 1 2 3 4 5 6 7 8 9 0 2 1 2 3 4 5 6 7 8 9 0 3 1 2 3 4 5 6 7 8 9 0 4 1 2 3 4 5 6 7 8 9 0

置換

一行目の0xに置換したい場合、下記の様になります。

1s,0,x x 1 2 3 4 5 6 7 8 9 0 %p x 1 2 3 4 5 6 7 8 9 0 1 1 2 3 4 5 6 7 8 9 0 2 1 2 3 4 5 6 7 8 9 0 3 1 2 3 4 5 6 7 8 9 0 4 1 2 3 4 5 6 7 8 9 0

全体かつ行内で見付かった1は全てXに置換する場合、下記の様になります。

%s,1,X,g %p x 1 2 3 4 5 6 7 8 9 0 X X 2 3 4 5 6 7 8 9 0 2 X 2 3 4 5 6 7 8 9 0 3 X 2 3 4 5 6 7 8 9 0 4 X 2 3 4 5 6 7 8 9 0

お判りの通り、ほとんどsedと同じですね。

まとめ

端末が壊れた場合に普段から備える意味でedの使い方を解説してみました。
追記と置換を知っていれば大抵は大丈夫ですね。

また、edは標準入力からコマンドを入力することが可能でプログラマブルに使えたりもします(これがsedに発展)

(echo '%s,33,66,g';echo 'w';echo 'q')|ed foo.txt

等々

他にも奥深い機能がedにはありますので、みなさん探求してみてください。

第五世代コンピューターの恩恵を社内インフラ業務でも享受したい

2015年12月17日
区分
AdventCalender
報告者:
chiba

こんにちは、chibaです。

アクトインディ Advent Calendar 2015 の17日目の記事です。

第五世代コンピュータープロジェクトとは、1980年代前半から当時の通産省のバックアップで進められた次世代のコンピューターの研究プロジェクト(ICOT)です。
この次世代のコンピュータを指して「第五世代コンピューター」なのですが、この 世代 とは、コンピュータの発展を世代に分けて表現したもので、

  • 第一世代: 真空管
  • 第二世代: トランジスタ
  • 第三世代: IC
  • 第四世代: LSI

と来て、第五世代は、多数のプロセッサによる超並列処理で人工知能だ!ワッショイ!という感じだったようです。

第五世代コンピュータープロジェクトでは、プログラミング言語には論理型が採用されましたが、並列プロセッサが前提であるため、並行論理プログラミング言語である、GHCやその派生であるKL1が生み出され、KL1はプロジェクトの核となる言語となりました。
このKL1をCで実装したものが、先週akiyama紹介したKLICになります。

社内インフラ業務でもKL1を

さて、前置きが長かったですが、日々の社内インフラ業務でも第五世代コンピューターの恩恵に浴したい訳です。
KLICが弊社システムでも動くようになったので、まずは手始めに日々のバックアップスクリプトをKL1で書いてみるかということになりました(私的に)。
ファイルのバックアップではrsyncを利用することが多いのですが、こんな感じに書けました。

:- module main. main :- Basedir="/backup/dir", Remotedir="user@remotehost:/files", joinstrings(["rsync -av --delete ", Remotedir, Base_dir, ">/dev/null && echo OK || echo Something went wrong."], " ", Cmd), unix:system(Cmd, 0).</p>

joinstrings([], Sep, Ans) :- Ans = "". joinstrings([Sa|Sd], Sep, Ans) :- join_strings(Sd, Sep, Ans1), generic:join(Sep, Ans1, Ans2), generic:join(Sa, Ans2, Ans). </code>

KLICには、unix:systemという述語がありますが、これが結構使い勝手が良いです。

さて、これをklicでコンパイルし、実行します。

$ klic -o backitup backitup.kl1

backitup という実行ファイルができるので、後は利用するのみです。ヤッター。

KLICで生成したバイナリの便利なところ

KLICはCへのトランスレータで、Cのソースをコンパイルして実行ファイルを生成しますが、生成されたバイナリには、基本的なオプションが付きます。
helpを始めとして、GCや並列実行時のオプションを渡すことが可能ですが、-tを付けることでトレースを掛けることが可能です。

例えば今回の場合は、

user@remotehost:~$ bin/backitup -t 1 CALL: main:main? 1 REDU: main:main :- 2 0:+join_strings(["rsync -..","user@rem..","/backup..",">/dev/n.."]," ",_6) 3 1:+unix:system(_6,0)? 2 CALL: main:join_strings(["rsync -..","user@rem..","/backup..",">/dev/n.."]," ",_6)? 2 REDU: main:join_strings(["rsync -..","user@rem..","/backup..",">/dev/n.."]," ",_6) :- 4 0:+join_strings(["user@rem..","/backup..",">/dev/n.."]," ",_1B) 5 1:+generic:generic(join(_1B,_14)," ") 6 2:+generic:generic(join(_14,_6),"rsync -..")? 4 CALL: main:join_strings(["user@rem..","/backup..",">/dev/n.."]," ",_1B)? 4 REDU: main:join_strings(["user@rem..","/backup..",">/dev/n.."]," ",_1B) :- 7 0:+join_strings(["/backup..",">/dev/n.."]," ",_2E) 8 1:+generic:generic(join(_2E,_27)," ") 9 2:+generic:generic(join(_27,_1B),"user@rem..")? 7 CALL: main:join_strings(["/backup..",">/dev/n.."]," ",_2E)? 7 REDU: main:join_strings(["/backup..",">/dev/n.."]," ",_2E) :- 10 0:+join_strings([">/dev/n.."]," ",_41) 11 1:+generic:generic(join(_41,_3A)," ") 12 2:+generic:generic(join(_3A,_2E),"/backup..")? 10 CALL: main:join_strings([">/dev/n.."]," ",_41)? 10 REDU: main:join_strings([">/dev/n.."]," ",_41) :- 13 0:+join_strings([]," ",_54) 14 1:+generic:generic(join(_54,_4D)," ") 15 2:+generic:generic(join(_4D,_41),">/dev/n..")? 13 CALL: main:join_strings([]," ",_54)? 13 REDU: main:join_strings([]," ","")? 14 CALL: generic:generic(join("",_4D)," ")? 14 REDU: generic:generic(join(""," ")," ")? 15 CALL: generic:generic(join(" ",_41),">/dev/n..")? 15 REDU: generic:generic(join(" ",">/dev/n.."),">/dev/n..")? 11 CALL: generic:generic(join(">/dev/n..",_3A)," ")? 11 REDU: generic:generic(join(">/dev/n.."," >/dev/..")," ")? 12 CALL: generic:generic(join(" >/dev/..",_2E),"/backup..")? 12 REDU: generic:generic(join(" >/dev/..","/backup.."),"/backup..")? 8 CALL: generic:generic(join("/backup..",_27)," ")? 8 REDU: generic:generic(join("/backup.."," /backu..")," ")? 9 CALL: generic:generic(join(" /backu..",_1B),"user@rem..")? 9 REDU: generic:generic(join(" /backu..","user@rem.."),"user@rem..")? 5 CALL: generic:generic(join("user@rem..",_14)," ")? 5 REDU: generic:generic(join("user@rem.."," user@re..")," ")? 6 CALL: generic:generic(join(" user@19..",_6),"rsync -..")? 6 REDU: generic:generic(join(" user@19..","rsync -.."),"rsync -..")? 3 CALL: unix:system("rsync -..",0)? OK 3 REDU: unix:system("rsync -..",0)?

こんな感じになりますが、これは良い機能ですね。

まとめ

KL1を社内インフラ業務に使うという話でした。
実際に日々のバックアップ作業にも投入し、第五世代コンピューティングの息吹きを感じることで日々の業務のモチベーションを高めています。
なお、今回の場合、KL1というより、ほぼPrologのコードで、並列性も全然活かしていませんが、今後の発展に期待しています。

Common Lisp製のブログはエンジニア求人に効果はあるか

2015年12月07日
区分
AdventCalender
報告者:
chiba

こんにちは、chiba です。
このtech.actindi.netブログもCommon Lisp化してから早6年半になります。
当時CTOのkomagataさんの求人戦略の一つで、尖ったブログで情報発信することにより尖った人への訴求効果を狙ったと記憶していますが※、Common Lispのウェブサーバーで運用するのに加え、デザインもmachidaさんの異様に尖ったデザインが入ったこともあり、周囲のエンジニアへの受けもかなり良かったと記憶しています。
勉強会等で知り合ったエンジニアさんと話をすると、『ああ、あの変なブログの会社』と言われたりすることは多かったですね。
ちなみに、当初は適当にCommon Lispのウェブサーバー上でhtmlのペライチで動かしているだけのものでした。
この怪しいブログの怪しい光に惹き寄せられてしまった現エンジニアとしてはtaharaakiyamaがおり、元は十分取れたかなと思いますが、これからも怪しい光を撒き散ら して欲しいものです。

Common Lisp製のブログはエンジニア求人に効果はあるか

ということで、実績からすると、Common Lisp製のブログはエンジニア求人に効果あり、と結論させて頂きます。

Haskell製にしろOCaml製にしろGauche製にしろ効果はあると思いますが、やっぱり何かしら面白いことをやってる所の方が良さそうに見えますよね。

そんな弊社ですが、あいかわらずプログラミングが大好きなエンジニア・プログラマを募集しています。

以上、お粗末でした。

※デザインによるユーザーフィルタリング - Fjord,LLC

Actindiの公用プログラミング言語は?

2015年12月05日
区分
AdventCalender
報告者:
chiba

こんにちは、chiba です。

アクトインディ Advent Calendar 2015 の5日目の記事です。

かれこれ10年位前に話題になった古い話ですが、かのGoogleでは社内で利用できる公用プログラミング言語が、Java、C++、Pythonと定められていて、これら以外の言語を利用する場合には特別な許可を必要としている(いた?)そうです。
最近ではGoogle発のGoなどもメジャーになってきたので最近はどうなのかは知りませんが、弊社も公用プログラミング言語を定めてみたいですね、ということで、まずは現状の利用状況を調べてみました。

使用言語プロジェクト篇

ざっとですが、現在の弊社のプロジェクトのレポジトリを眺める限りでは、

  1. Ruby
  2. JavaScript
  3. CoffeeScript
  4. Shell
  5. Common Lisp
  6. TeX
  7. Objective-C
  8. Java

辺りの言語が使われています。
弊社ではRailsプロジェクトが多数ということもあり、Rubyが圧倒的な主力と言って良い感じです。

これらのうちプロジェクトのメインになっている言語は、

  1. Ruby
  2. Common Lisp
  3. Objective-C
  4. Java

ですが、Common Lispは、このブログであったり、いこーよ内のリコメンド機能の実現であったり、ログ解析システムで使われています。
Objective-C、Javaは、スマホ関連です。
また、JavaScript系、Shell系は大体Railsプロジェクトのお供といった所です。

過去を振り返ってみると、PHP辺りもありました。

使用言語ハウスキーピング篇

ハウスキーピングでは、大体がShell、Common Lisp、Ruby大好きな人も多いのでRuby、という感じになっています。
分析などにRもちょっと利用されているようです。
過去には無駄にシェルスクリプトをScsh(Scheme)で書いたりする人もいました(私)

Actindiの公用プログラミング言語は?

上記の結果を眺める限り、Actindiの公用プログラミング言語を決めるとしたら、RubyとCommon Lispでしょうか。
とりあえず、そうしておきましょう。

ちなみに、社内では、無類のプログラミング言語オタクも半数位いるため、機能するなら何の言語で書いても全然怒られたりしません。
そんな感じなので私は隙があればCommon Lispをねじ込んでいますが、今後、OCamlやHaskell、Clojureのコードが加わることを期待しています。

なお、Common Lispの開発環境を準備するのがめんどいという声はたまに耳にするので、その辺りの整備は弊社の今後の課題かもしれません。

以上、お粗末でした。

Common Lispで作る日報環境

2015年10月13日
区分
日報
報告者:
chiba

こんにちは、chibaです!
このブログでは過去に日報を如何に書くかについて度々エントリーが書かれていますが、今回も日報生成の話です。

以前は当日こなしたタスクを眺めたりして振り返る感じでしたが、タスク管理と日報の作成で仕事を二重にしている感があったので、思い切って社内のイシュー管理システムのRedmineの情報から日報を生成することにしてみました。

準備

タスクデータ取得

日報を書くには、現在抱えている仕事と、当日こなした仕事の2つの情報が必要ですが、RedmineのAPIだけでは、上手く取得できないようです。
ということでカスタムクエリを設定して、これを呼ぶことにしました。

送信

Yammerや社内メールに送信しますが、Yammerにはメール投稿用のアドレスがあるので、どちらもメールで投稿することにします。

コード

最近念願のLispWorksを購入したので無駄にGUIのインターフェイスを付けてみました。
折角なのでRedmineの状態に合せて更新するボタンも付け、弊社標準ウィンドウマネージャーのStumpWMのコマンドも作成します。

daily-report

以上を合わせてこんな感じになります。

(cl:in-package :stumpwm)

(ql:quickload :cl-json :silent T)
(ql:quickload :drakma :silent T)
(ql:quickload :cl+ssl :silent T)
(ql:quickload :cl-smtp :silent T)

(defvar *firefox-path* "/usr/bin/firefox")

(defun browser (uri)
  (run-shell-command
   (format nil "~A ~S" *firefox-path* uri)))

(defun redmine-client (id)
  (let ((drakma:*text-content-types* '(("application" . "json"))))
    (drakma:http-request
     (format nil "https://example.com/issues/~A.json" id)
     :additional-headers '(("X-Redmine-API-Key" . "...")))))

(defun issue-subject-from-id (id)
  (json:json-bind (issue.subject) (redmine-client id)
                  issue.subject))

(defun my-issues ()
  (let ((drakma:*text-content-types* '(("application" . "json"))))
    (drakma:http-request
     "https://example.com/issues.json?query_id=NN"
     :additional-headers
     '(("X-Redmine-API-Key" . "...")))))

(defun my-issues/closed-today ()
  (let ((drakma:*text-content-types* '(("application" . "json"))))
    (drakma:http-request
     "https://example.com/issues.json?query_id=NN"
     :additional-headers
     '(("X-Redmine-API-Key" . "...")))))

(defun iso-date ()
  (multiple-value-bind (s- m- h- d m y)
                       (get-decoded-time)
    (declare (ignore s- m- h-))
    (format nil "~4,'0D-~2,'0D-~2,'0D" y m d)))

(defun daily-report-string ()
  (let ((date (iso-date)))
    (with-output-to-string (out)
      (format out "~2&お疲れ様です、千葉です。")
      (format out "~2&~Aの日報をお送りします。" date)
      (format out "~2&【勤務時間】11:00〜17:00")
      (format out "~2&【本日の作業内容】")
      (json:json-bind (issues)
                      (my-issues/closed-today)
    (when issues
      (format out "~2& 【終了】~2%")
      (dolist (i issues)
        (format out
            "~3,' D% #~A ~A~%"
            (cdr (assoc :done--ratio i))
            (cdr (assoc :id i))
            (cdr (assoc :subject i))))))
      (json:json-bind (issues)
                      (my-issues)
    (setq issues
          (sort issues
            #'>
            :key (lambda (x)
               (cdr (assoc :done--ratio x)))))
    (when issues
      (format out "~2& 【実行中】~2%")
      (dolist (i issues)
        (unless nil ;; (zerop (cdr (assoc :done--ratio i)))
          (format out
              "~3,' D% #~A ~A~%"
              (cdr (assoc :done--ratio i))
              (cdr (assoc :id i))
              (cdr (assoc :subject i)))))))
      (format out "~2&【感想】~2%"))))

(defun send-yammer (sub msg)
  (cl-smtp:send-email "smtp.example.com"
              "送り主のアドレス"
              "Yammerのメール投稿アドレス"
              sub
              msg 
              :ssl :tls
              :authentication
              '(:login "foouser" "foopass")))

(defconstant tue 1)

(defun tomorrow-off-p ()
  (= tue (nth 6 (multiple-value-list (get-decoded-time)))))

(defun declare-off-day (offp msg)
  (block nil
    (or offp (return msg))
    (let ((dcl (format nil "~%※明日水曜日はお休みです。~%")))
      (and (search dcl msg) (return msg))
      (concatenate 'string msg dcl))))

(capi:define-interface daily-report ()
  ()
  (:panes
   (editor-pane
    capi:editor-pane
    :flag 'daily-report
    :text (declare-off-day (tomorrow-off-p) (daily-report-string))
    :buffer-name :temp
    :echo-area-pane echo-area
    :visible-min-width '(character 80)
    :visible-min-height '(character 15))

   (buttons
    capi:push-button-panel
    :items '("Send Report" "Update")
    :callback-type :data
    :selection-callback 
    (lambda (command)
      (cond ((string= "Send Report" command)
             (when (capi:confirm-yes-or-no "この内容で送信しますか?")
               (send-yammer (format nil
                                    "~1&【日報 システムチーム/千葉】 ~A"
                                    (iso-date))
                            (capi:editor-pane-text editor-pane))
               (capi:apply-in-pane-process editor-pane
                                           #'capi:quit-interface
                                           editor-pane)))
            (T (when (capi:confirm-yes-or-no "タスク内容を更新しますか?")
                 (let* ((text (capi:editor-pane-text editor-pane))
                        (playback (search "【感想】" text))
                        (updated-text (daily-report-string)))
                   (setf (capi:editor-pane-text editor-pane)
                         (declare-off-day (tomorrow-off-p)
                                          (if playback
                                              (concatenate 'string
                                                           (subseq updated-text
                                                                   0
                                                                   (+ (+ -2 (- #.(length "【感想】")))
                                                                      (length updated-text)))
                                                           (subseq text playback nil))
                                              updated-text))                       
                         )))))))
   (echo-area capi:echo-area-pane :max-height t))
  (:menus (quit "Quit" ("Quit") :callback (lambda (name self) 
                                            (declare (ignore name))
                                            (capi:quit-interface self))))
  (:menu-bar quit)
  (:default-initargs :title "Daily Report" :auto-menus nil))

(defcommand daily-report () ()
     ""
  (execute-command-or-raise 'daily-report1
                            '(:title "Daily Report")))

(defcommand daily-report1 () ()
     ""
  (let ((ept (make-instance 'daily-report)))
    (capi:display ept)))

どうもコードに重複が多いですが、まあこれで良いだろうということで完成。
30行程度でGUIが付くなんてLispWorksは素晴しいですね。

Common LispでS3

2015年09月11日
区分
s3
報告者:
chiba

こんにちは、システム管理業として出戻ってきましたchibaです。
前回は、なんでもbrowse-url-at-pointというのを書きましたが実に5年前のようです。時の流れが早い…。

さて今回は、S3上のログをCommon Lispで取得してみたことについて書きます。

Common LispでS3を利用する場合ですが、ありがたいことに zs3というライブラリがあるので、これを利用しました。
ログをダウンロードしてくるだけなら2、30行で書けちゃうかなと思います。
書いてみた感想としては、やっぱensure-directories-exist 便利だな、という所です。

ちなみに、awsというpython製ツールもありますが、pythonのツールを揃えないといけないのでQuicklispの環境を整えるのに比べてもどっこいどっこいでしょう(多分)

別に大したことはないツールはこちらです。

今後もシステム管理でCommon Lispを活用していきたいと思います。

なんでもbrowse-url-at-point

2010年11月11日
区分
Emacs
報告者:
chiba

こんにちは、Chibaです!
ネタ切れなので、ちょっとした自前便利Emacs lispの紹介です!
browse-url-at-pointとは、ポイント位置にURLの文字があれば、それをブラウザで開くというものです。 このbrowse-url-at-pointで使われている、thing-at-pointという関数が肝なのですが、この関数は、ポイント(カーソル)がある場所のオブジェクトを取得できるというEmacsの関数です。
この関数なのですが使い方次第では非常に便利です。
browse-url-at-pointでは、URLが決め打ちですが、thing-at-point(ポイント位置のオブジェクトを取得)とbrowse-url(ブラウザで開く)を組み合せることによって似たようなものを簡単に作成することができます。
例えば、ポイント位置のRedmineのチケット(#1234というような形式)を開きたい場合は、色々手抜きですが、

(defun show-ticket-at-point ()
  (interactive)
  (browse-url (format "https://example.com/issues/show/%s"
                      (thing-at-point 'word))))

のように書けると思います。
簡単な割には便利ですので、エディタ上の情報からブラウザで何か開きたいと思った時には、工夫して色々作成してみてはいかがでしょう。

コッカを移動する

2010年11月10日
区分
Emacs
報告者:
chiba

こんにちは、Chibaです!
今日もネタが無さ過ぎるのでぼんやり考えたEmacs小ネタで行きます!
自分は、括弧は先に対で入力する派なのですが、先に入力するとコッカの方を後ろに移動したり前に持って行きたくなったりします。
具体的には、

(a) b c d e f

を、

(a b c d e f)

にしたくなったりするわけですね。
これは良いブログネタ!ということで早速作ってみました。

(define-key global-map [(control meta shift ?f)]
  (defun forward-thesis (arg)
    (interactive "p")
    (when (string= ")" (thing-at-point 'char))
      (delete-char 1)
      (forward-sexp arg)
      (insert ")")
      (backward-char))))

(define-key global-map [(control meta shift ?b)]
  (defun backward-thesis (arg)
    (interactive "p")
    (when (string= ")" (thing-at-point 'char))
      (delete-char 1)
      (backward-sexp arg)
      (backward-char)
      (insert ")")
      (backward-char))))

ちょっと妙な動きもしますが、使えるようであればちゃんとしたものに作り直そうかなと思っています。
ちなみに、thesisという名前は、paren/thesisに分ける呼び方があるようなので、これから取りました。
jargon, node: ASCII

elispを書き散らかすあばれん坊M-x doctor

2010年11月04日
区分
Emacs
報告者:
chiba

こんにちは、Chibaです!
ネタが無さ過ぎるのでぼんやり考えたEmacs小ネタで行きます!

Emacsの初期化を細かく関数に分ける

そのままな内容ですが、初期化するコード片を追加する度に関数にして書き散らし、大本の.emacsから呼ぶようにします。
具体的には、

(defun setup>grep ()
  (require 'grep-edit)
  (setq grep-find-command "/usr/local/bin/ack --nocolor --nogroup "))
という風に定義し、.emacsから、(setup>grep)のように呼ぶという風。
なんのメリットもなさそうですが、find-function-at-pointで定義先に飛べるので、気儘に書き散らかしても、一発でジャンプできます(要バイトコンパイル)。
これで書き散らかしても安心。もっと書き散らかしたい!

バッファのファイル名をキルリングに入れる

これまた、そのままな内容ですが、編集しているバッファのファイル名をタスクのチケット等に貼りたことが多いので、

(defun kill-new-current-bufffer-file-name ()
  (interactive)
  (kill-new (buffer-file-name)))
こういう風なものを作ってみました。地味に便利です。
以上、小ネタ2つでした。

Emacsで直前で閉じたバッファをまた開きたい

2010年10月26日
区分
Emacs
報告者:
chiba

こんにちは、chibaです!
Firefoxを使っていると良くお世話になるre-open tab(Ctrl+Shift+T)ですが、Emacsでも欲しくなりました。
ということで早速作ってみましたが、バッファをそのまま復元するというのは分からなかったので、前回閉じたファイルだけ復活できるというバージョンで。
バッファを復元できる方法が分かれば、それを試してみたいですね。

;; utils
(defmacro aif (test then &optional else)
  `(let ((it ,test))
     (if it
         ,then
         ,else)))

(defmacro awhen (test &rest body)
  `(aif ,test
        (progn
          ,@body)))

(progn
  ;; re-open-file
  (defvar *recent-buffers*)
  (setq *recent-buffers* () )
  
  (defadvice kill-buffer (before kill-buffer-before activate)
    (awhen buffer-file-name
      (push it *recent-buffers*)))
  ;; (ad-deactivate 'kill-buffer) 
  
  (defun re-open-file ()
    (interactive)
    (aif *recent-buffers*
         (find-file (pop it))
         (message "null")))
  
  (define-key global-map [(control x) (shift ?k)]
    're-open-file) )

Cimy Swift SMTP + Contact Form 7でJISのメールが送れない

2010年10月18日
区分
WordPress
報告者:
chiba

こんにちは、chibaです!
ネタ切れなので、問題の本質的な解決がされてないネタを書きます。
タイトルの通りなのですが、Cimy Swift SMTP + Contact Form 7でJISのメールを送ろうと設定していましたが、WP Multibyte Patchの設定をJISにしてもさっぱりJISでメールが送信されません。
何かがどこかで競合しているのかと思い、プラグインを削除していきましたが、Cimy Swift SMTPを抜いたところJISの設定が効くようになりました。
今回の場合は、そもそもCimy Swift SMTPは必要なかったので削除してしまうことにしましたが、今後どうしても必要になった際には調べてみたいと思います。

4つ単位で増加するuniversal-argumentで大丈夫か

2010年10月18日
区分
Emacs
報告者:
chiba

こんにちは、chibaです!
ネタ切れなので、どうでも良い感じのEmacsの日々を書きます。

Emacsのユーザーには、お馴染の数引数を入力するC-uですが、デフォルトだと(* 4)な感じで増加していきます。

================================================================

は、C-u C-u C-u =の様に入力したりしていますが、自分は便利だなと思うのはこれ位です。
どちらかというと一個ずつ増加していった方が便利なのではないだろうか、ということで、オリジナルのコードを改造して作ってみました。
C-uと置き換えてみても良かったのですが、super+uということにしてみましたが、どんな感じなのかしばらく様子を見てみたいと思います。

(defun universal-argument-1+ ()
  (interactive)
  (setq prefix-arg (list 1))
  (setq universal-argument-num-events (length (this-command-keys)))
  (ensure-overriding-map-is-bound))

(defun universal-argument-1+-more (arg)
  (interactive "P")
  (cond ((consp arg)
         (setq prefix-arg 
               (list (if (minusp (car arg))
                         (1- (car arg))
                         (1+ (car arg))))))
        ((eq arg '-)
         (setq prefix-arg (list -1)))
        ('T (setq prefix-arg arg)
            (restore-overriding-map)))
  (setq universal-argument-num-events (length (this-command-keys))))

(define-key global-map [(super u)]
  'universal-argument-1+)

(define-key universal-argument-map [(super u)]
  'universal-argument-1+-more)

使い捨てなコマンドを作っては捨て

2010年10月04日
区分
Emacs
報告者:
chiba

こんにちは、chibaです!
本当にネタ切れなので、どうでも良いレベルのEmacsの日々を書きます。
emacsのVCモードでは、gitが使えるのですが、git pushはしてくれないらしく、これだけシェルで実行していたりしました。
いやいやこれでは、いけないということで、コマンドを作成。
default-directoryにパスを束縛すれば、そのディレクトリで実行される様子(この辺あまり良く分かっていないのですが…)
ちなみに、無駄にマクロにしています。コマンド名にはスペースも使えるようなので、LispWorksのエディタみたいなコマンド名にできるようなマクロです。
anything.elで絞り込むのでこういう名前でも問題ないみたいですね。

(defmacro defcommand (name lambda-list &rest forms)
  (let* ((lambda-list (copy-list lambda-list))
         (iarg (getf lambda-list '&interactive)))
    `(progn
       (defun ,(intern name) ,(progn (remf lambda-list '&interactive) lambda-list)
         (interactive ,iarg)
         ,@forms)
       ,name)))

(defcommand "Git Push" ()
  (let ((default-directory (file-name-directory (buffer-file-name))))
    (shell-command "git push") ))

Edit with EmacsとStumpWMの連携が便利!

2010年09月24日
区分
Emacs
報告者:
chiba

こんにちは、chibaです!
Emacs大好きな自分は、Firefoxのテキストエリアの編集には、It’s All Text!を利用しているのですが、最近Chromeを使うことも多く、It’s All Text!みたいなのが、Chromeにもないかなあとちょっと探してみたらEdit with Emacsというのをみつけました。
It’s All Text!と違ってこちらは Edit with Emacs という名前の通りEmacs専用という感じです。
Emacs側で、サーバーを立てて接続を待ち受けるという方式なので、専用のelispを読み込ませておく必要があります。
いつものパターンなのですが、ChromeからEmacsを呼び出したときは、フォーカスをEmacsに遷移、そして編集が終わればまたChromeにフォーカスを自動で戻したいところ。
StumpWMならば、シェルからコマンドを実行できる仕組みがあるので、これでフォーカスの遷移に対応してみました。

(progn
  (require 'edit-server)

  (setq edit-server-new-frame nil)

  (unless edit-server-new-frame
    (add-hook 'edit-server-done-hook
              (lambda ()
                (start-process "->chrome" nil "stumpish" "chrome"))))
      
  (add-hook 'edit-server-start-hook
            (lambda ()
              (start-process "->emacs" nil "stumpish" "emacs")))
  
  (edit-server-start) )

Edit with Emacsと連携させる、edit-server.elですが、フックを掛けられるように作り込まれていて、楽に拡張ができます。この辺りに地味に感心しました。 ■

自分好みにウィンドウ切り換え (1)

2010年09月16日
区分
Emacs
報告者:
chiba

こんにちは、chibaです!
ネタ切れなので、Emacsをいじります。
Emacsを利用している方で、 ElScreenを使っている方は多いと思います。
自分も確かに便利だなあと思うのですが、どうも挙動が自分の好みに合わないところがあり、代りにEmacsのレジスタ機能を使ってみたりしていました。(逆にややこしいですが…)
先日ふと、ウィンドウの構成はどういう風に保存/復帰しているのかを調べてみたら、(current-window-configuration)で構成を取得、set-window-configurationで設定という感じでした。
思いのほか単純だったので、自分好みの挙動をするようなものを作って行ってみようかと思います。
とりあえずで、window-configuration-listという循環リストにウィンドウの構成を保存するだけの素朴なものをでっち上げてみました。循環リストなので、くるくる回ります。
これだけでも割と使えていますが、もうちょっと便利にしたいですね。

;; 循環リストを作成する
(defun circular-list (&rest elts)
  (cdr (rplacd (last elts) elts)))

(defvar window-configuration-list)

(defun init-window-configuration-list ()
  (setq window-configuration-list
        (circular-list (current-window-configuration))))

;; window-configurationをリストにプッシュ
(defun push-window-configuration ()
  (interactive)
  (push (current-window-configuration)
        (cdr window-configuration-list))
  (message (format "created (%d)"
                   (window-configuration-list-size window-configuration-list))))

;; FIXME eqで比較するだけ
(defun window-configuration-list-size (wc)
  (let ((start wc)
        (cnt 1))
    (catch 'count
      (mapl (lambda (x)
              (when (eq start x)
                (throw 'count nil))
              (incf cnt))
            (cdr wc)))
    cnt))

(defun cycle-window-configuration-list ()
  (interactive)
  ;; 現在の窓の状態で更新
  (setcar window-configuration-list
          (current-window-configuration))
  ;; 順送り
  (setq window-configuration-list
        (cdr window-configuration-list))
  ;; 次の要素を現在の窓設定にする
  (set-window-configuration
   (car window-configuration-list)))

(defun delete-window-configuration ()
  (interactive)
  (setcar window-configuration-list
          (cadr window-configuration-list))
  (setcdr window-configuration-list
          (cddr window-configuration-list))
  (set-window-configuration
   (car window-configuration-list))
  (message (format "deleted (-> %d)"
                   (window-configuration-list-size
                    window-configuration-list)))) )

(progn
  ;; keybind
  (define-key global-map [(control meta ?l)] 'cycle-window-configuration-list)
  (define-key global-map [(control meta shift ?i)] 'push-window-configuration)
  (define-key global-map [(control meta ?!)] 'delete-window-configuration) )

(init-window-configuration-list)

Rubyでなにもしない

2010年09月09日
区分
Ruby
報告者:
chiba

こんにちは、chibaです!
最近、文字列を場合に応じてUTF-8(デフォルト)と、sjisに切り換えて文字を生成したいということがありました。
自分としては、

"こんにちは".send(if sjis? then :tosjis else なにもしない end)

と書きたかったのですが、Rubyでなにもしないメソッドってなんだったかなーと探してもなかなか見付かりません。 Haskellでいうid、LISP系では、identityというのが多いのですが、ruby identityでググったら、

  • Ruby 1.9 - Feature #841: Object#self - Ruby Issue Tracking System
    という一連のスレッドがみつかりました。
    なるほど、提案はされているもののリジェクトされてるんですね。
    高階手続きに馴染んでいる人ならば、identityの有用性は体験していると思うのですが、馴染みのない人からは、identityみたいに何もしないでそのまま受け取った値を返すものなんて何に使うの?という意見は割とFAQかなと思います。
    この議論中で
class Object
  def identity() self; end
end

というのが例として挙げられていました。これを使えば、

"こんにちは".send(if sjis? then :tosjis else :identity end)

"こんにちは".send(sjis? ? :tosjis : :identity)

と書ける訳ですね。
それで結局自分はどうしたかというと、Objectにメソッドを定義するのも気が引けたので

str = "こんにちは"
if sjis? then 
  str.tosjis
else
  str
end

とか、

str = "こんにちは"
sjis? ? str.tosjis : str

と書くことにしました。
これはこれで妥当ですね。 ■

空白一つにこだわりたい(2)

2010年09月03日
区分
Emacs
報告者:
chiba

こんにちは、chibaです!
完全にネタ切れなのですが、毎週1つのエントリーペースは守って行きたいので連投です。
質より量でまいります。
以前、空白一つにこだわりたいというエントリーを書きましたが、割と日々便利に使っています。
最近はちょっと違ったところで、空白を一つにしたい状況に遭遇しています。

- [ ]  こんにちは   あいうえお    かきくけこ
- [ ]   こんばんは  あいうえお   かきくけこ
- [ ]  おはよう  あいうえお   かきくけこ

という感じなのですが、何かから行をコピペしてくると発生することが多いです。
これをちまちま直しているのが非常に手間ということで、適当に、elispを書いてみました。

(defun just-one-space-each ()
  (interactive)
  (save-excursion
    (let* ((begin (progn (beginning-of-line) (point)))
           (end (progn (end-of-line) (point)))
           (begin (min begin end))
           (end (max begin end)))
      (goto-char begin)
      (while (and (re-search-forward " +" nil t) (<= (point) end))
        (replace-match " ")))))

各行で実行すると

- [ ] こんにちは あいうえお かきくけこ
- [ ] こんばんは あいうえお かきくけこ
- [ ] おはよう あいうえお かきくけこ

という風に空白1つに統一できます。快適!
ちなみに、Twitterのつぶやきで最近 delete-indentation (M-^)というのを教えてもらいました。
行の連結に使えるのですが、just-one-spaceの使いどころと似ています。 ■

マニュアル/ソースを良く読もう!

2010年09月03日
区分
Emacs
報告者:
chiba

こんにちは、chibaです!
完全にネタ切れなので、中途半端に失敗談を書きます。
skype.elは非常に便利なのですが、自分のEmacsの操作が鈍い所為かチャットの切り換えにいつも、もたもたしておりました。
このままではいけないと思った私は、便利elispを書いて対応することに。
社内のSkypeの利用形態としては、グループチャットが主で使われる部屋も2つ位と少ないので、このチャットをキー一発で開ければOKであろうと思われました。
ということで、skype.elをちらちら見つつ、

(require 'cl)

(defun *skype-find-ai-chat (name)
  (let ((chat (find-if (lambda (x) 
                         (search name
                                 (aref x 2)))
                       (skype--chat-get-recent-objects))))
    (when chat
      (switch-to-buffer
       (skype--open-chat-buffer chat)))))

(defun skype-open-konnichiwa-chat ()
  (interactive)
  (*skype-find-ai-chat "こんにちは"))

(defun skype-open-kombanwa-chat ()
  (interactive)
  (*skype-find-ai-chat "こんばんは"))

(defun skype-open-oyasuminasai-chat ()
  (interactive)
  (*skype-find-ai-chat "おやすみなさい"))

(define-key global-map [(super ?1)] 'skype-open-konnichiwa-chat)
(define-key global-map [(super ?2)] 'skype-open-kombanwa-chat)
(define-key global-map [(super ?3)] 'skype-open-oyasuminasai-chat)

のような物を書いてみました。
これで、Super-1〜2で目的の部屋を一発で開くことができます。
これは割と便利だなーと思いつつ、改めて、skype.elを眺めたところ、

(global-set-key (kbd "M-9") 'skype--anything-command)

とするとanything.elと連携できて良いよ!との解説が。
試してみたところ、断然こっちの方が便利でした。
やはりREADME的なものはちゃんと読もう、と思った次第です。

低価格レンタルサーバーの共有SSLとWordPress (2)

2010年08月27日
区分
WordPress
報告者:
chiba

こんにちは、chibaです!
完全にネタ切れなので、中途半端にWordPressのことを書きます。
続きものだった筈の低価格レンタルサーバーの共有SSLとWordPress (1)を書いてから半年程放置してしまいましたが、WordPressも3系が出たということでこのテーマについて少しまとめてゆくことにしてみました。
前回は1つのWordPress+低価格レンタルサーバーでSSLサイトと通常のサイトをどう切り盛りするかというのがテーマでしたが、色々試してみた結果、1つのWordPressでがんばらずにフォーム専用のWordPressを別個にインストールして使うことにするのが一番手間がかからないようです。
ちょっとした問題としては、SSL専用でWordPressを利用する場合、WordPress2系ではどういう訳かSSLの状態では管理画面にまともにログインできませんでした。
大抵のところでは、URLがhttpでもhttpsでも使えるので管理画面のログインはhttpでサイトの利用はhttpsという謎な運用をすることにより回避できていましたが、WordPress3系では解決しているようです。これは嬉しいところですね。
非常に中途半端ですが、次回に続きます…

隙があればlispを詰め込んで行きたい (9)

2010年08月23日
区分
LISP
報告者:
chiba

こんにちは、chibaです!
隙があればlispを詰め込んで行く日々ですが、古めのウェブページの累計カウンター的な物を月に一回更新するという作業が発生しました。
sedで一発かなと思いましたが、sedで一発と思ったらCLで書くことにしているので、CLで書いてみます。
具体的な内容ですが、index.htmlの中に、カウンター的なものがあり、数字はそれぞれ1枚のGIF画像になっているのを、毎月集計された数字に更新というところです。
サーバーとは、SSHで通信できます。
折角なので、リモートのファイルをローカルのファイルと同じ感覚で編集できるようなマクロを作成してみました。
といってもリモートからscpしてきて編集するだけのものです。
これと、以前に定義したオレオレsedを組み合わせて任務は完了です。

(exec:define-executable scp)

(defmacro with-output-to-remote-file ((stream path) &body body)
  (let ((temp-file-name (string (gensym "/tmp/with-output-to-remote-file-"))))
    `(unwind-protect (progn
                       (with-open-file (,stream ,temp-file-name :direction :output)
                         ,@body)
                       (scp ,temp-file-name ,path)
                       nil)
       (when (cl-fad:file-exists-p ,temp-file-name)
         (delete-file ,temp-file-name)))))

(defmacro with-input-from-remote-file ((stream path) &body body)
  (let ((temp-file-name (string (gensym "/tmp/with-input-from-remote-file-"))))
    `(unwind-protect (progn
                       (scp ,path ,temp-file-name)
                       (with-open-file (,stream ,temp-file-name)
                         ,@body)
                       nil)
       (when (cl-fad:file-exists-p ,temp-file-name)
         (delete-file ,temp-file-name)))))

(defun カウンター (num)
  (fare-utils:join-strings
   (list
    "<!-- カウンター -->" 
    "<div id=\"count\">" 
    "<table id=\"counter\">" 
    "<tr>" 
    "<td>" 
    (format nil
            "~{<img src=\"./images/count_~a.gif\" alt=\"\" />~%~}"
            (map 'list #'values (write-to-string num)))
    "</td>" 
    "<td> <img src=\"./images/count_ken.gif\" alt=\"\" width=\"18\" height=\"17\" /></td>" 
    "</tr>" 
    "</table>" 
    "</div>"
    "<!-- /カウンター -->")
   :separator #\Newline))
;; 実行例
(with-output-to-remote-file (out "example:public_html/index.html")
  (with-input-from-remote-file (in "example:public_html/index.html")
    (sed "<!-- カウンター -->"
         "<!-- /カウンター -->" 
         (カウンター 12345)
         :IN IN
         :OUT OUT)))


隙があればlispを詰め込んで行きたい (8)

2010年08月06日
区分
StumpWM
報告者:
chiba

こんにちは、chibaです!
隙があればlispを詰め込んで行く毎日を連投です。
このシリーズの6回目で本日が祝日であるかどうかを判定したい、というネタを投稿しましたが、そもそも何がしたかったかというと、業務時間かどうかによってFirefoxや、Skypeのプロファイルを切り替えられるようにしてみたら便利かな、というのが動機でした。
ということで、この前のHOLIDAY-Pを拡張して、IN-WORKING-HOURS-Pというのを作成し、就業時間中は、仕事用のプロファイルでアプリを起動することにしてみました。
そういえば今迄説明なしで来てしまいましたが、もちろんウィンドウマネージャーはStumpWMです!

(DEFUN IN-WORKING-HOURS-P ()
  (AND (NOT (HOLIDAY-P))
       (< 6
          (NTH-VALUE 2 (DECODE-UNIVERSAL-TIME (GET-UNIVERSAL-TIME)))
          17)))

;; firefox
(DEFCOMMAND FIREFOX () ()
  "Run or switch to firefox."
  (IF (IN-WORKING-HOURS-P)
      (run-or-raise "/usr/bin/firefox -P \"work\" " '(:class "Firefox"))
      (run-or-raise "/usr/bin/firefox -P \"home\"" '(:class "Firefox"))))

これで、会社で同僚にFirefoxを使って説明をしているときに、アッー!なURLの履歴などが出てきてしまいアッー!っとなることを防げますね。 ■

隙があればlispを詰め込んで行きたい (7)

2010年08月06日
区分
LISP
報告者:
chiba

こんにちは、chibaです!
隙があればlispを詰め込んで行く毎日ですが、今日の課題は、

  "8/24(火)、
8/25(水)、
8/26(木)、
8/27(金)
8/31(火)、
9/1(水) 、
9/2(木) 、
9/3(金)
9/7(火) 、
9/8(水) 、
9/9(木) 、
9/10(金)
9/14(火)、
9/15(水)、
9/16(木)、
9/17(日)"

のような、曜日付き日付の文字列が与えられた場合に、日付に対応している曜日が正しいかの判定です。
日付はそれほどでもないですが、曜日は結構間違ってたりすることも多いですよね。

(SET' FOO
  "8/24(火)、
8/25(水)、
8/26(木)、
8/27(金)
8/31(火)、
9/1(水) 、
9/2(木) 、
9/3(金)
9/7(火) 、
9/8(水) 、
9/9(木) 、
9/10(金)
9/14(火)、
9/15(水)、
9/16(木)、
9/17(日)")

(PPCRE:DO-REGISTER-GROUPS ((#'PARSE-INTEGER MON) (#'PARSE-INTEGER DATE) DOW)
    ("(\\d+)/(\\d+)\\((.+)\\).*" FOO)
  (LET* ((CDOW (NTH-VALUE 6 (DECODE-UNIVERSAL-TIME (ENCODE-UNIVERSAL-TIME 0 0 0 DATE MON 2010))))
         (X (FORMAT NIL "~D/~D (~A)" MON DATE (ELT "月火水木金土日" CDOW)))
         (Y (FORMAT NIL "~D/~D (~A)" MON DATE DOW)))
    (FORMAT T "~A => ~A~%" X Y)
    (UNLESS (STRING= X Y)
      (ERROR "曜日が間違っています! (正)~A => (誤)~A~%" X Y))))

動作:

8/24 (火) => 8/24 (火)
8/25 (水) => 8/25 (水)
8/26 (木) => 8/26 (木)
8/27 (金) => 8/27 (金)
8/31 (火) => 8/31 (火)
9/1 (水) => 9/1 (水)
9/2 (木) => 9/2 (木)
9/3 (金) => 9/3 (金)
9/7 (火) => 9/7 (火)
9/8 (水) => 9/8 (水)
9/9 (木) => 9/9 (木)
9/10 (金) => 9/10 (金)
9/14 (火) => 9/14 (火)
9/15 (水) => 9/15 (水)
9/16 (木) => 9/16 (木)
9/17 (金) => 9/17 (日)
;>>> 曜日が間違っています! (正)9/17 (金) => (誤)9/17 (日)

こんな感じのものを書いてしのいでみました。
大体の内容ですが、単純に正規表現で切り出してシステムから割り出したものと一致するかを見ているだけです。
Common Lispで適当に書き捨てましたが、どちらかというとelispで書いた方が役立つ機会は多いかもしれません。 ■

Emacsで複数のバッファのファイル名を控えておきたいとき

2010年07月30日
区分
Emacs
報告者:
chiba

こんにちは、chibaです!
Emacsで色々な場所にちらばっている複数のバッファを編集しつつ、編集したファイルだけ名前を控えておきたいとき、皆さんはどのように対処されてますでしょうか。
多分、本当にたまにしか必要に迫られることはないと思うのですが、
自分の考えた解決法は、
M-:して

Eval: (push (buffer-file-name) foo)

してfooにファイル名を蓄積して、scratchなどで、fooを評価して一覧を得るというものです。

foo ;; C-u C-x C-eなど
("/home/mc/tmp/g13237.del" "/home/mc/work/log/log-2010-07-30.org" ...)

とりあえず、これでしのげましたが、なんとなくEmacs標準の方法がありそうですねー。

ERBでのコメントアウトについて

2010年07月26日
区分
ERB
報告者:
chiba

こんにちは!、千葉です!
RailsのViewでは、ERBが使われていますが、#や、=begin、=endの範囲コメントは入れ子になった場合に使い勝手が悪いことが多いですよね。
皆さんざっくりコメントアウトするときには、どういう方法を使っているのでしょうか。
自分は、とりあえず、

<%- if false -%>
コメントアウトしたいこと
<%- end -%>

と書いたり、

def comment(&body)
end

というのを定義して、

<%- comment { -%>
コメントアウトしたいこと
<%- } -%>

などとしてみたりでしのいでいますが、もっと楽な方法はある気がしてなりません。

隙があればlispを詰め込んで行きたい (6)

2010年07月21日
区分
LISP
報告者:
chiba

非常にどうでも良い理由で今日が祝日であるかどうかを判定したくなりました。
休日の判定は思ったより色々面倒そうですが、動機がどうでも良いことだけに簡単に済ませたいところ。
ということで、休日情報のAPIが公開されていないか調べたところ、いくつかみつかったのですが、Google Calendarからjsonで手軽に情報を取得できるようなので、これを使ってみることにしました。

(DEFUN GET-CALENDAR-JSON (UT)
  (LET ((REQUEST-URL 
         (KMRCL:MAKE-URL 
          "full"
          :BASE-DIR "http://www.google.com/calendar/feeds/japanese@holiday.calendar.google.com/public/"
          :VARS `(("start-min" . ,(XYZZY:FORMAT-DATE-STRING "%Y-%m-%d" UT))
                  ("start-max" . ,(XYZZY:FORMAT-DATE-STRING 
                                   "%Y-%m-%d"
                                   (+ UT (* 24 60 60))))
                  ("max-results" . "1")
                  ("alt" . "json-in-script")
                  ("callback" . "handleJson")))))
    (STRING-TRIM "handleJson();"
                 (SB-EXT:OCTETS-TO-STRING
                  (DRAKMA:HTTP-REQUEST REQUEST-URL :FORCE-BINARY 'T)))))

(DEFUN -> (LIST &REST KEYS)
  (IF (ENDP KEYS)
      LIST
      (KMRCL:AWHEN (FIND (CAR KEYS) LIST :KEY #'ZL:CAR-SAFE)
        (APPLY #'-> KMRCL:IT (CDR KEYS)))))

(DEFUN HOLIDAY-P (&OPTIONAL (UT (GET-UNIVERSAL-TIME)))
  (LET ((DAY (NTH-VALUE 6 (DECODE-UNIVERSAL-TIME UT))))
    (OR (<= 5 DAY)
        (< 0
           (CDR
            (-> (JSON:DECODE-JSON-FROM-STRING (GET-CALENDAR-JSON UT))
                :FEED
                :OPEN-SEARCH$TOTAL-RESULTS
                :$T))))))
(HOLIDAY-P)
;⇒ 今日が休日ならT

(HOLIDAY-P (ENCODE-UNIVERSAL-TIME 0 0 0 21 7 2010))
;⇒ NIL

(HOLIDAY-P (ENCODE-UNIVERSAL-TIME 0 0 0 19 7 2010))
;⇒ T

どうやらGoogle Calendarだと今年分しか情報が取得できない様子なのですが、当日が休日なのかどうかを判定できさえすれば良いのでこれでOKとします。

WordPressとActiveRecord

2010年07月14日
区分
ActiveRecord
報告者:
chiba

WordPressのデータを一括でいろいろと変更する際には、SQLでいろいろすると思うのですが、もしかしてActiveRecordを使えたら割と便利だったりするのかな、ということでActiveRecordからWordPressのDBに接続してみたりしていました。
しかし、これは既に試してみている人が絶対いそうだなと思ったので軽くググってみたところ、やはり、そのものズバリがありました。
github: jystewart / wp_ar
こんな感じで使えます。

irb(main):254:0> WpUser.count
12

150行弱のコードですが、機械的に変換する作業には割と使えるかもしれないなと思いました。
自分も、CLSQLなどで同じようなものを組んでみたいです。 ■

隙があればlispを詰め込んで行きたい (5)

2010年07月08日
区分
報告者:
chiba

隙があればlispを詰め込んで行きたい日々ですが、今回は一つのファイルを複数ファイルに分割する仕事です。
こういうのはやっぱりsed/awkの仕事だろう、と思うのですが、一つLISPでがんばりたいところ
具体的な仕事内容ですが、

  1. 複数のエントリーがまとめられた、複数の長いテキストファイルを入力とする
  2. エントリーの区切り文字は^_
  3. エントリーごとに1ファイルにして出力 </ol> というところです。
    書き捨てな感じですが、
    (dolist (file (directory "/var/tmp/foo/bar/big-*.txt"))
      (dolist (mail (cl-utilities:partition-if 
                     (mycl-util:curry #'string= "^_")
                     (kmrcl:read-file-to-strings file)))
        (alexandria:with-output-to-file 
            (out (format nil "/var/tmp/result/~A" (gensym "foo-")))
          (print out)
          (dolist (line mail)
            (write-line line out)))))
    
    のように書いてみました。
    軽く解説すると、
    1. ファイル読み込み1行を要素とするリストにする
    2. PARTITION-IFでリストの中身をデリミタで1ファイル1リストとして分割する
    3. 分割した要素1リストを1ファイルとして出力。名前が被らないようにGENSYMで名前をつける
    4. </ol> という風にしてみました。
      想像していたより短く書けたので満足です。

隙があればlispを詰め込んで行きたい (4)

2010年06月29日
区分
lisp
報告者:
chiba

隙があればlispを詰め込んで行きたい日々ですが、今回はWordPressのデータをいじる仕事です。
仕事の内容ですが、WordPressにはカスタムフィールドという便利機能があり、ここに入力したデータが便利にページに表示されます。
このカスタムフィールド内のHTMLを大量に変更することになりました。
具体的には、

select * from wp_postmeta where meta_key = 'こんにちは画像';

で目的の内容が取得できるので、このテキストの内容を置換して同じ場所に戻してやる、ということになります。
SQL文を書いて色々すれば良いのですが、CLSQLを使ってみることにしました。

(asdf:oos 'asdf:load-op :clsql)
(in-package :clsql-user)

(connect '("localhost" "db" "wp" "")
         :database-type :mysql)
  
(execute-command "set character_set_client='utf8'")
(execute-command "set character_set_connection='utf8'")
(execute-command "set character_set_results='utf8'"))

;; DBから読み出してSQLを出力
;; <a href=...>を<a target="_blank" href=...>に変更

(let ((ahref (ppcre:create-scanner "(<a)(\\s+)(href=.*)"))
      (window.open (ppcre:create-scanner "window.open")))
  (alexandria:with-output-to-file (out "/tmp/foo.txt")
    (do-query ((meta_id post_id meta_key meta_value)
               "select * from wp_postmeta where meta_key = 'こんにちは画像';")
      (cond ((ppcre:scan "window.open" meta_value)
             ;; 既にwindow.openで開くような指定があるばあいはスルー
             :nop)
            ('T (let ((new-val
                       (ppcre:regex-replace 
                        ahref
                        meta_value
                        (lambda (match &rest registers)
                          (declare (ignore match))
                          (destructuring-bind (a b c) registers
                            (format nil
                                    "~a target=\"_blank\"~a~a"
                                    a
                                    b
                                    c)))
                        :simple-calls 'T)))
                  (format out
                          "update wp_postmeta set meta_value = '~A' where meta_key = 'こんにちは画像' and post_id = ~A;~%" 
                          new-val
                          post_id))))))) 

内容としては、

  • CLSQLを準備
  • MySQLに接続
  • 文字コードをUTF-8に設定(任意)
  • select文を発行して結果をリストで受けとる
  • リストの内容から、目的の文を取り出しPPCRE(Common LispのPerl正規表現互換パッケージ)で処理
  • ファイルにSQL文を書き出し

今回は目的のSQLサーバーに直接接続するのが面倒だったので、ファイルにSQL文を書き出しましたが、CLSQLで接続して変更ということも勿論できます。
また、”select〜”みたいなことになっていますが、(select […])のようにも書けるようです。
色々適当ですが、とりあえず目的は達成できました

隙があればlispを詰め込んで行きたい (3)

2010年06月22日
区分
LISP
報告者:
chiba

隙があればlispを詰め込んで行きたい日々ですが、今回はウェブ上のHTMLの内容確認の仕事です。
具体的には、h1、title、meta keywords、meta descriptionが記述されたCSV形式のファイルがあり、これの内容どおりになっているかをチェックする、という内容。
とりあえず、HTMLのタグの抜き出しには、Drakmaと、closure-htmlを使って、欲しいタグの内容を抜き出す関数を作成してみました。
|((:h1 …) (:keywords …) (:description …) (:title …))|という変な名前ですが、title keywords description h1を抜き出して返す関数名が思い付かなかったので返す結果の形をそのまま名前にしています。Common Lispは名前に記号も使えるので思考が停止したときに便利ですね。

(defun |((:h1 ...) (:keywords ...) (:description ...) (:title ...))| (url)
  (let* ((page (drakma:http-request url))
         (doc (chtml:parse page (cxml-stp:make-builder)))
         (ans () ))
    (stp:do-recursively (a doc)
      (when (and (typep a 'stp:element)
                 (or (string-equal (stp:local-name a) "title")
                     (string-equal (stp:local-name a) "meta")
                     (string-equal (stp:local-name a) "h1")))
        (let* ((kwd-or-dsc (cond ((string-equal (stp:attribute-value a "name")
                                                "description")
                                  `(:description
                                    ,(stp:attribute-value a "content")))
                                 ((string-equal (stp:attribute-value a "name")
                                                "keywords")
                                  `(:keywords
                                    ,(stp:attribute-value a "content")))))
               (tag (intern (string-upcase (stp:local-name a)) :keyword))
               (svalue (stp:string-value a))
               (tem () ))
          (cond ((and (string= "" svalue)
                      (eq :meta tag))
                 :nop)
                ('T (push svalue tem)
                    (push tag tem)))
          (when kwd-or-dsc
            (push (cadr kwd-or-dsc) tem)
            (push (car kwd-or-dsc) tem))
          (and tem (push tem ans))
          )))
    ans))

この|((:h1 …) (:keywords …) (:description …) (:title …))|を使って

(progn
  (print '////////////////////////////////////////////////////////////////)
  (dolist (x (fare-csv:read-csv-file "foo.csv"))
    (destructuring-bind (url title kwd dsc h1) x
      (let* ((url (ppcre:regex-replace "://www.example.com" ;CSVファイルのURL
                                       url
                                       "://www.example.net")) ;実際のサーバーのURL
             (q (|((:h1 ...) (:keywords ...) (:description ...) (:title ...))| url)))
        (flet ((*check (var key)
                 (string= var (second (assoc key q)))))
          (cond ((and (*check h1 :h1)
                      (*check kwd :keywords)
                      (*check dsc :description)
                      (*check title :title))
                 :nop)
                ('t
                 (flet ((frob (var key)
                          (list key
                                (if (*check var key)
                                    :ok
                                    (list :ng var (second (assoc key q)))))))
                   (print '*******************************************)
                   (print url)
                   (print (frob h1 :h1))
                   (print (frob kwd :keywords))
                   (print (frob dsc :description))
                   (print (frob title :title))))))))))

のように殴り書きしてみました。キーワードが一致していないと

////////////////////////////////////////////////////////////////
*******************************************
"http://www.example.com/foo/bar" 
(:H1 (:NG "こんにちは" "Routing Error")) 
(:KEYWORDS (:NG "なるほど" NIL)) 
(:DESCRIPTION (:NG "なんのことですか?" NIL)) 
(:TITLE (:NG "mjd!" "Action Controller: Exception caught")) 
...

のような結果がREPLに出てきます。
なんだか長いですけど、Common Lispで書いても実行を確認しながら書けるので、そんなに大変でもありません。

隙があればlispを詰め込んで行きたい (2)

2010年06月12日
区分
LISP
報告者:
chiba

こんにちは、chibaです。
今回もネタがないのでLISPネタです。
日常の作業では、テキストファイルを加工することが割とあったりすると思いますが、そういう時には、sedや、awkって便利ですよね。
ちょっとした一行野郎が大活躍、ということは結構あると思います。
自分もそういう一行野郎が好きではあるのですが、いやしかし、隙があればlispを詰め込んで行きたい。
最近もsedを使いたくなるようなHTMLの編集作業に遭遇しました。
「指定したディレクトリ以下に含まれるすべてのHTMLファイルの<div class="foo"から、</div>の間を、用意したテキストファイルの内容と置き換える。」
という仕事です。
sedで一発だろうと思いましたが、とりあえず、Common Lispで書いてみました。

(LOOP :FOR FILE :IN
   (DIRECTORY "/tmp/foo/**/*.html")
   :DO
   (WITH-OPEN-FILE (IN FILE)
     (WITH-OPEN-FILE (OUT (FORMAT NIL "~A.new.html" (PATHNAME FILE))
                          :DIRECTION :OUTPUT
                          :IF-EXISTS :SUPERSEDE
                          :IF-DOES-NOT-EXIST :CREATE)
       (LOOP :WITH OPEN 
          :FOR LINE := (READ-LINE IN NIL NIL) :WHILE LINE
          :DO (PROGN
                (WHEN (SEARCH "<div class=\"foo\">" LINE)
                  (SETQ OPEN 'T))
                (COND ((AND OPEN (SEARCH "</div>" LINE))
                       (SETQ OPEN NIL)
                       (WITH-OPEN-FILE (IN2 "g000001/foo.html")
                         (LOOP :FOR LINE := (READ-LINE IN2 NIL NIL) :WHILE LINE
                            :DO (WRITE-LINE LINE OUT))))
                      ((NOT OPEN)
                       (WRITE-LINE LINE OUT)))
                )))))

気分は一行野郎なのでコードもいきあたりばったりで汚ないです。
これで仕事は片付いたのですが、率直な感想として、sedに比べると書くのがかなりめんどくさいです。(一切ライブラリを使ってないということもありますが…)
こんなにめんどうでは、やっぱりCommon Lispよりsedを使ってしまいます。
いやしかし、こういう道具を沢山書き溜めておいて、必要なときにさっと出せるようになれば、そのうちsedやawkではなく、自然にCommon Lispを使うようになるかもしれません。
ということで、若干無理はあるものの、コードを纒めて次の機会に備えておくことにしました。

(DEFUN SED (START-PAT END-PAT NEW 
            &KEY (IN *STANDARD-INPUT*) (OUT *STANDARD-OUTPUT*))
  (LOOP :WITH OPEN 
        :FOR LINE := (READ-LINE IN NIL NIL) :WHILE LINE
        :DO (PROGN
              (WHEN (SEARCH START-PAT LINE)
                (SETQ OPEN 'T))
              (COND ((AND OPEN (SEARCH END-PAT LINE))
                     (SETQ OPEN NIL)
                     (WRITE-LINE NEW OUT))
                    ((NOT OPEN)
                     (WRITE-LINE LINE OUT))))))

(DEFUN MAP-FILE-INTO (FILES FUNCTION)
  (LET ((TEMPNAME-SUFFIX (GENSYM "TEMP-FILE-")))
    (DOLIST (FILE FILES)
      (LET ((TEMPFILE-NAME (FORMAT NIL "~A_~A" FILE TEMPNAME-SUFFIX)))
        (WITH-OPEN-FILE (IN FILE)
          (WITH-OPEN-FILE (OUT TEMPFILE-NAME
                           :DIRECTION :OUTPUT
                           :IF-EXISTS :SUPERSEDE
                           :IF-DOES-NOT-EXIST :CREATE)
            (FUNCALL FUNCTION IN OUT)))
        (RENAME-FILE TEMPFILE-NAME FILE)))))

この2つを使えば、今回の仕事は、

(MAP-FILE-INTO (DIRECTORY "**/*.html")
               (LAMBDA (IN OUT)
                 (SED "<div class='foo'>"
                      "</div>"
                      (KL:READ-FILE-TO-STRING "foo.txt")
                      :IN IN
                      :OUT OUT)))

のように書けます。
いまのところぱっとしませんが、そのうちCommon Lispだけでテキスト仕事は片付けられるようになることを目指します!

macroexpandでHTMLを書く

2010年06月02日
区分
Emacs
報告者:
chiba

こんにちは、chibaです。
今回もネタがないのでEmacsネタです。
Dylanのマクロについての文献:
http://people.csail.mit.edu/jrb/Projects/dexprs.pdf を読んでいて、XMLや、LISPは、Skeleton Syntax Tree Representationsに分類されるというのを読んで、そうか、HTMLも簡単にマクロ書けたりするのかもと思ったのでEmacs Lispでちょっと試してみました。

(defun split-id-or-class (string)
  (let ((elts (split-string string "[\\.#]")))
    (if (cdr elts)
        elts
        string)))

(defmacro defhtmltag (tag)
  (let* ((tag-str (symbol-name tag))
         (tags (split-string tag-str "[\\.#]"))
         (tag-name (car tags))
         (attr (cadr tags)))
    `(defmacro ,tag (&rest body)
       `(list ,(concat
                "<"
                ,tag-name 
                (apply #'concat 
                       (cond ((find ?. ,tag-str)
                              (list " class=\"" ,attr "\""))
                             ((find ?# ,tag-str)
                              (list " id=\"" ,attr "\""))
                             ('T (list ""))))
                ">")
              ,@body
              ,(concat "</" ,tag-name ">")))))

(defun flatten (lis)
  (cond ((atom lis) lis)
        ((listp (car lis))
         (append (flatten (car lis)) (flatten (cdr lis))))
        (t (append (list (car lis)) (flatten (cdr lis))))))

(defmacro with-html-output-to-string (&rest body)
  `(reduce (lambda (x y) (format "%s%s" x y))
           (flatten (list ,@body))))

使い方としては、

(defhtmltag li.foo)

のようにすると、

(list "<li>" "こんにちは" "</li>")

のようなものにマクロが展開されるので、リストの入れ子を平坦にして一つの文字列に繋げれば完成です。
ちょっと込み入ったところだと

(defhtmltag table)
(defhtmltag tbody)
(defhtmltag td)
(defhtmltag tr)

(defun list-to-table (list)
  (table 
   (tbody
    (mapcar (lambda (x)
              (tr
               (mapcar (lambda (y) (td y))
                       x)))
            list))))

(insert
 (with-html-output-to-string
  (list-to-table '((foo bar baz quux)
                   (1 2 3 4)
                   (z z z z)))))
;=> <li class="foo"><li class="foo"><li class="foo"><li class="foo">foo</li><li class="foo">bar</li><li class="foo">baz</li><li class="foo">quux</li></li><li class="foo"><li class="foo">1</li><li class="foo">2</li><li class="foo">3</li><li class="foo">4</li></li><li class="foo"><li class="foo">z</li><li class="foo">z</li><li class="foo">z</li><li class="foo">z</li></li></li></li>

ということもできます。
ちなみに、マクロ展開でHTML書くって新しいかも!と思いましたが、別にマクロにしなくても関数でも書けることに今気付きました…
とりあえず、もし、HTMLにマクロがあったとしたらLISPと似た感じになるのかなあという印象は持ちました。

隙があればlispを詰め込んで行きたい

2010年05月25日
区分
Emacs
報告者:
chiba

こんにちは、chibaです。
今回もネタがないのでEmacsネタです。 日常のちょっとしたテキストの編集作業なのですが、

app/views/top/foo.html.erb
app/views/shared/_foo.html.erb
app/views/shared/foo.html.erb
app/views/top/_foo.html.erb
app/views/shared/foo.html.erb
app/views/layouts/application.html.erb
config/links/foo.html.erb
public/images/shared/foo.jpg

のようなテキストをスペース区切りの一行にまとめる必要に迫られました。
ここはEmacsで、改行をスペースに置換してやれば良いんじゃないかと思いますが、いや、ここは踏ん張ってlispを書いてゆきたいと思い、

(mapconcat (lambda (x) (format "%s" x))
'(
app/views/top/foo.html.erb
app/views/shared/_foo.html.erb
app/views/shared/foo.html.erb
app/views/top/_foo.html.erb
app/views/shared/foo.html.erb
app/views/layouts/application.html.erb
config/links/foo.html.erb
public/images/shared/foo.jpg
)
" ") 

のようなものを書いて、式のにカーソルを持っていって、C-u C-x C-eです。
そうすると、式の後ろに

=> "app/views/top/foo.html.erb app/views/shared/_foo.html.erb app/views/shared/foo.html.erb app/views/top/_foo.html.erb app/views/shared/foo.html.erb app/views/layouts/application.html.erb config/links/foo.html.erb public/images/shared/foo.jpg"

のようなものが挿入されます。lambdaって書けて良かったですよね。
次に、

A    app/views/top/foo.html.erb
M    app/views/shared/_foo.html.erb
M    app/views/shared/foo.html.erb
M    app/views/top/_foo.html.erb
?    app/views/shared/foo.html.erb
M    app/views/layouts/application.html.erb
A    config/links/foo.html.erb
M    public/images/shared/foo.jpg

のようなテキストでファイル名の部分だけ、一行にスペース区切りでつなげる必要に迫られました。
ここは、Emacsで、kill-rectangleでしょうか。いや、ここは踏ん張ってlispを書いてゆきたいと思い、

(apply #'concat
(loop for (x y) on 
'(
A    app/views/top/foo.html.erb
M    app/views/shared/_foo.html.erb
M    app/views/shared/foo.html.erb
M    app/views/top/_foo.html.erb
?    app/views/shared/foo.html.erb
M    app/views/layouts/application.html.erb
A    config/links/foo.html.erb
M    public/images/shared/foo.jpg
)
by #'cddr
collect (format "%s " y))
)

のようなものを書き、上と同じく、eval-last-sexp すると

"app/views/top/foo.html.erb app/views/shared/_foo.html.erb app/views/shared/foo.html.erb app/views/top/_foo.html.erb app/views/shared/foo.html.erb app/views/layouts/application.html.erb config/links/foo.html.erb public/images/shared/foo.jpg "

という文字列が得られます。
なるほど、これは、shell-command-on-regionで、awk ‘{if($1==”M”){printf(“%s “, $2);}}’とかしている場合ではないですね。
Emacs便利です。

EmacsでGoogle Chart

2010年05月20日
区分
Emacs
報告者:
chiba

こんにちは、chibaです。
今回もネタがないのでEmacsで遊んでいたネタを。
Googleは、便利にグラフを書けるAPIであるGoogle Chart APIというのを提供しています。
URLを指定するだけで、グラフが書けたり、QRコードが書けたり色々便利なものですが、数値のリストからスタックされた棒グラフを書いてみようと思ったので、ちょっと遊んでみました。

(defun* gchart-stack-bar-chart-url (data &key (style :horizontal))
  (let ((len (length data)))
    (concat
     "http://chart.apis.google.com/chart?"
     "cht=b" (case style
               (:horizontal "hs")
               (otherwise "vs")) 
     "&chd=t:"
     (let* ((totals (mapcar (lambda (x)
                              (apply #'+ x))
                            data))
            (max (apply #'max totals))
            (to-string (lambda (x) (format "%s" x))))
       (mapconcat 
        to-string
        (mapcar 
         (lambda (x)
           (mapconcat
            to-string x ","))
         (apply #'mapcar*
                #'list
                (mapcar
                 (lambda (x) 
                   (mapcar
                    (lambda (y)
                      (format "%.1f"
                              (* (/ 100 max) y)))
                    x))
                 data)))
        "|"))
     "&chco=ff0000,00ff00,0000ff"
     "&chs=350x"
     (int-to-string (* len 28)))))
(let ((data '((10 20 30)
              (3 50 1)
              (1 2 3)
              (33 33 33)
              (5 6 7)
              (33 33 33)
              (3 3 9))))
  (browse-url (gchart-stack-bar-chart-url data)))

これで、

のようなグラフが書けます。
タイトルも付けられるようですが、飽きてしまいました
Emacs便利です。

確認恐怖症をEmacsが救う

2010年05月14日
区分
Emacs
報告者:
chiba

こんにちは、chibaです。
ネタがないので今回もEmacsの小ネタです。
Emacsには、LISPが搭載されているので、ちょっとしたLISPを書くことでも色々便利に使えます。
代表的なところとしては、ちょっとした計算でしょうか

(+ 1500
  200
 3000
 4500
)
;⇒ 9200

S式の前置記法がこういう場合には便利です。
最近、SSLの申請で、更新するCSRを確認することがあったのですが、こういう確認の為の文字列比較にも使えることに気付きました。

(equal
"-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCSlAxEzARBgNVBAgTClNvbWUtU3RhdGUx
ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAM7k/tno3FquqOOrhkFxCiAVx/0qPjjcOhcD0PXx
ZCgSd6vHK0oSjDrWl+KFRMISd+EECBIrWR0I/u3RywKmlj1Q29wgt+UzFFJSK5+k
wsiQTnxs//uAqWoLBUUU3Y0/67P+cJSqflAcGgt8imhXEh2GzfPPKfB9nK3P4MZ7
WcrKvTwUEewASdaBWU+8+4ic8JzIwiCLge6tQvf/xeqkBhp7Othsf9vmwGARjDaI
DPSswlaKqkaXEog9fhhJmO2QUzEQ9R92MNmxu+wXHKxx3LuC4dvnoNNFz1l+cPq9
3wVoLBIaEBw8U06+BEPtlAf8ZRGYVG7LpxvhyS0EdDirN0MCAwEAAaAAMA0GCSqG
SIb3DQEBBQUAA4IBAQAF/Stfy58MQFRCmXZxaiKK1Ez9CBdyBtbCM+xquYzfRTMg
EEmGRXoZ1FomV2B2avkUU0frOnb3cktfuttBs1olwKvf3lzktdRoQBPrzDy7iAej
p4MwQHEmFgVXe3ZstjoYIQ2PudSPUlCQRUAi9oiNjVt0iAY7w1xnlCMqYTzGBW4y
8MpnVDKtTmmMPM0wxtiKDkOmT3S8L/mR1wNDnpavv5hOIn8z7No+qHzkLcuHEHYF
IVn9suw6L2GvPgxdw4XarWoFjeuADhszwwVpI87oGMH3H5Z6kniW22bQX1l402dO
YqSaxgasSPwJi+3njg0upYERIV7i9fR74/tufNv7
-----END CERTIFICATE REQUEST-----"

"-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCSlAxEzARBgNVBAgTClNvbWUtU3RhdGUx
ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAM7k/tno3FquqOOrhkFxCiAVx/0qPjjcOhcD0PXx
ZCgSd6vHK0oSjDrWl+KFRMISd+EECBIrWR0I/u3RywKmlj1Q29wgt+UzFFJSK5+k
wsiQTnxs//uAqWoLBUUU3Y0/67P+cJSqflAcGgt8imhXEh2GzfPPKfB9nK3P4MZ7
WcrKvTwUEewASdaBWU+8+4ic8JzIwiCLge6tQvf/xeqkBhp7Othsf9vmwGARjDaI
DPSswlaKqkaXEog9fhhJmO2QUzEQ9R92MNmxu+wXHKxx3LuC4dvnoNNFz1l+cPq9
3wVoLBIaEBw8U06+BEPtlAf8ZRGYVG7LpxvhyS0EdDirN0MCAwEAAaAAMA0GCSqG
SIb3DQEBBQUAA4IBAQAF/Stfy58MQFRCmXZxaiKK1Ez9CBdyBtbCM+xquYzfRTMg
EEmGRXoZ1FomV2B2avkUU0frOnb3cktfuttBs1olwKvf3lzktdRoQBPrzDy7iAej
p4MwQHEmFgVXe3ZstjoYIQ2PudSPUlCQRUAi9oiNjVt0iAY7w1xnlCMqYTzGBW4y
8MpnVDKtTmmMPM0wxtiKDkOmT3S8L/mR1wNDnpavv5hOIn8z7No+qHzkLcuHEHYF
IVn9suw6L2GvPgxdw4XarWoFjeuADhszwwVpI87oGMH3H5Z6kniW22bQX1l402dO
YqSaxgasSPwJi+3njg0upYERIV7i9fR74/tufNv7
-----END CERTIFICATE REQUEST----"
)
;⇒ nil

Emacs便利です。

letの束縛部分を便利に編集したい

2010年05月06日
区分
Emacs
報告者:
chiba

こんにちはchibaです。 ネタ切れなのでいきなりLISPネタなのですが、letでつつまれた式を編集している際に、後で束縛したい変数を追加したくなることって良くありますよね。

(let ((str "foo bar baz"))
  ....

  (setq len (length str)))

という風に書いていて、後でlenが欲しくなるような
つまり

(let ((str "foo bar baz")
      (len 0))
  ....

  (setq len (length str)))

こういう風に前に戻って(len 0)を追加したいわけですね。
思い立ったが吉日なのでそういうelispを書いてみることにしました。

(progn
  (defun edit-let-bind ()
    (interactive)
    (let ((foundp nil))
      (save-excursion
        (catch 'loop
          (while (not (c-at-toplevel-p))
            (backward-up-list)
            (down-list)
            (let ((thing (thing-at-point 'symbol)))
              (cond ((or (string= "let" thing)
                         (string= "do" thing))
                   (down-list)
                   (setq foundp t)
                   (throw 'loop nil))
                  ('T (backward-up-list))))))
        (when foundp
          (recursive-edit)))))
  ;; keybind
  (define-key global-map [(control meta shift ?c)] 'edit-let-bind))

非常に適当な作りですが、letや、doの束縛部を編集するために、再帰編集モードに入り、束縛部分にカーソルを飛し、編集が終わったら、再帰編集モードから抜ける、という風にしてみました。
再帰編集モードから抜けるのが、C-M-cだったので、C-M-sh-cで、束縛部の編集へ。
一応それなりに便利に使えますが、どなたかもっとちゃんとした、elispの流儀に則ったものを作成して頂けると嬉しいです! ■

空白一つにこだわりたい

2010年04月28日
区分
Emacs
報告者:
chiba

こんにちは、chibaです。
ネタがないので今回もEmacs lispの小ネタです。
文字列やS式をだらだら編集し、最後に空白一つ区切りで整形しようとしたときなのですが、間に改行が挟まっていると、just-one-spaceがきかないので、kill-lineしたりして調整することになります。

"foo
   bar
      baz"
=>
"foo bar baz"

自分はこれが非常にもどかしかったので、just-one-spaceを拡張したものを作成してみました。
といっても、just-one-spaceのソースが空白とタブしか飛していなかったので改行も加えただけです。

(defun just-one-space-to-next-sexp (&optional n)
  (interactive "*p")
  (let ((orig-pos (point)))
    (skip-chars-backward " \t\n")
    (constrain-to-field nil orig-pos)
    (dotimes (i (or n 1))
      (if (or (= (following-char) ?\s))
	  (forward-char 1)
	(insert ?\s)))
    (delete-region
     (point)
     (progn
       (skip-chars-forward " \t\n")
       (constrain-to-field nil orig-pos t)))))
;; キーバインド例
(define-key  global-map [(meta shift ?\s)] 'just-one-space-to-next-sexp)

これで、すぱっと隣りに揃えられるので気持良いです。
リージョンやリストの中身を処理してくれる関数もあったら便利かもしれません。

次の日のファイルも作りたい

2010年04月22日
区分
Emacs
報告者:
chiba

自分は、以前も「Emacsでスクラッチファイルを日付順にして管理するライフハック」で紹介したように日付を元にファイル名を付けて、スクラッチファイルやブログの下書きなどを書いているのですが、たまに、次の日付のファイルが欲しくなることがあります。
これはブログのネタになりそうだということで、そういうファイルを作成するEmacs Lispを適当に書いてみました。
自分は、Emacs Lispを書くときは、なんとなく(require ‘cl)したくないのですが、欲しい関数を調べるのが億劫だったのでべたべたに(require ‘cl)になってしまいました。

(require 'cl)

(defun buffer-name-prefix ()
  (let ((buffer-name (buffer-name (current-buffer))))
    (substring buffer-name 0 (position-if #'digit-char-p buffer-name)) ))

(defun prefix-Y-m-d.suffix-to-list (string)
  (destructuring-bind 
      (prefix Y m &optional d)
      (split-string string "-")
    (when (and (null d)
               (every #'digit-char-p prefix) )
      (shiftf d m Y prefix ""))
    (destructuring-bind
        (d &optional suffix)
        (split-string d "\\.")
      (list prefix Y m d (or suffix "")) )))

(defun next-file-name (name)
  (destructuring-bind 
      (prefix Y m d suffix)
      (prefix-Y-m-d.suffix-to-list name)
    (let ((time (encode-time 0
                             0
                             0
                             (parse-integer d)
                             (parse-integer m)
                             (parse-integer Y)
                             nil
                             0) ))
      (concat (if (string= "" prefix) "" (concat prefix "-"))
              (format-time-string "%Y-%m-%d" (progn (incf (first time)) time))
              (if (string= "" suffix) "" (concat "." suffix)) ))))

(defun make-next-file ()
  (interactive)
  (find-file (next-file-name (buffer-name))) )

使い方ですが、foo-2000-02-28.txtのようなファイル名のバッファで作業している時に(make-next-file)すると、foo-2000-02-29.txtのようなファイルをfind-fileします。
バッファの名前は、{prefix-}yyyy-mm-dd{.suffix}のようなものを想定していて、これ以外では上手く動きませんが、それなりに便利に使えています。Emacs便利です。 ■

Redmine_Importer便利です

2010年04月16日
区分
Redmine
報告者:
chiba

こんにちは、chibaです。
社内では、タスク管理にRedmineを導入していて、システムの人から、全然システムじゃない人まで利用しています。
Redmineは便利なのですが、定期的なチケットや、まとめての登録が面倒になってきました。
以前、Redmine勉強会のUstreamを眺めていて、csvから一括で登録できるというプラグインを紹介していたことを思いだしたので早速導入してみることにしました。 junoさんの発表で色々一括登録する方法が紹介されていますが、junoさんが日本語化されているRedmine_Importerというものを導入してみることにしました。
インストールすると、プロジェクトの設定画面のモジュールのところから有効にできるモジュールとしてImporterが現われますので、これをオンにすると利用できます。
これで大量のチケットも楽に登録できます。ありがたや! ■

It's all textとCodaの連携

2010年04月08日
区分
Coda
報告者:
chiba

chibaです。
WordPressで作業していると、フォーム内のHTMLを編集する必要が出てくることが割と多いのですが、フォーム内の貧弱な編集機能であれこれするのはとても面倒です。
こういう時に自分が愛用しているのは、FireFoxのアドオンのIt’s All Text!です。
結構便利なので、フォームの中身はことごとく呼び出したEmacsから編集しているのですが、便利なのでシステム系の人以外のデザイナーさん達にもお薦めしたいです。
そんな感じで、社内でも布教していたのですが、Codaとの組み合わせで上手く動かなかったので、メモ。
Codaには、コマンドラインから起動できる便利なCommand-Line Codaという便利スクリプトがあるのですが、これとの組み合わせで上手く動かない現象に遭遇
原因を探ってみたところ、どうやら、ファイルのパス名にスペースが入ってしまっているのが原因ではないかということで、

#!/bin/sh

/usr/local/bin/coda `echo $*|sed 's/ /\\ /g'`

のような適当なスクリプトを作成して、/usr/local/bin/its-all-codaのような名前で保存し、これをIt’s All Text!から呼ぶことにしてみたところ解決。
sed便利です。

WordPressのプラグイン Contact Form 7 が便利だった件

2010年03月25日
区分
WordPress
報告者:
chiba

こんにちは、chibaです。
現在、WordPressを通してPHPの文化を勉強中です。
アンケートや申込みのフォームを作成することは度々あるかと思うのですが、WordPressでそれを実現する定番プラグインにContact Form 7というのがあることを知りました。 今回、Contact Form 7を利用するにあたり、利用したい機能をどうやって設定するかを調べてみたのでメモ。

自動返信のサンクスメールを飛したい

Contact Form 7では、通知用とその他の予備で、2通の通知メールを出すことができます。
このメール(2)の設定で、申込者のメールアドレスを送り先に指定すれば、自動返信のサンクスメールを設定することが可能でした。便利ですね。

サンクスページを設置したい

WordPress管理画面から お問い合わせ > その他の設定 の欄に、

on_sent_ok: "location.replace('サンクスページのURL');"

と記述することによって、で正常に送信できた後のリダイレクトを設定することができます。便利ですね。

UTF-8ではなく、JIS(iso-2022)のメールを送信したい

これは、Contact Form 7の設定ではないのですが、大体組み合せで発生する問題だと思うので、ここに書いておきたいと思います。
WordPressではデフォルトの設定では、UTF-8のメールが飛ぶようです。最近のメーラーだとUTF-8のメールも普通に読めますが、対応してない環境も多数あるかと思います。
ということで、UTF-8ではなく、JISにしたいのですが、変更するには、 WP Multibyte Patch というプラグインで設定するのが定番のようです。
設定ファイルのサンプルが-sample.phpとして付属してきますので、これをリネームして利用します。

wp-content/plugins/wp-multibyte-patch/wpmp-config-sample.php
=>
wp-content/plugins/wp-multibyte-patch/wpmp-config.php
wp-content/plugins/wp-multibyte-patch/ext/ja/config-sample.php
=>
wp-content/plugins/wp-multibyte-patch/ext/ja/config.php

という感じにリネーム。
wp-content/plugins/wp-multibyte-patch/ext/ja/config.php の内容は、

<?php
/* WP Multibyte Patch extension config file */

// Set mail mode.
// "auto" picks  "jis" or "UTF-8" automatically.
// "jis" or "UTF-8" fix the encoding method to its name.
$wpmp_conf['mail_mode'] = 'jis';   // auto, jis, UTF-8

?>

ですが、ここでjisを指定することになるようです。リネームしたら、プラグインを有効化し、テストしてJISでメールが届いているかを確認します。
色々な人が様々な便利/拡張機能を提供していてWordPress/PHP素晴らしいですね。 ■

思い込み、こわいです

2010年03月12日
区分
WordPress
報告者:
chiba

こんにちは、chibaです。
いきなりWordPressの話なのですが、私はこれまで、WordPressとは、HTML内のリンクというリンクにhttp〜というURLをベタベタと詰め込めるだけ詰め込む生き物だと思っておりました。
これが故に、作成したサイトを別のURLを持つテストサイト等にコピーする際にも、SQLで色々するはめになるものだと思っておりました。
「せめてURLじゃなくて”/”から始まってくれれば良いのに…」と思いながらDBをいじっておりましたが、ふと、SQLでsiteurlに”/”を入れてみたところ普通に”/”からという記述で動くことが分かりました。
おやおや?ということで、この状態で、

設定 > 一般設定 > WordPress のアドレス (URL)

を眺めたところ、内容は空になっておりました。ものは試しに”/”を入力してみましたが、どうも末尾の”/”は消されて空になる様子
なんだWordPress内でURL指定じゃなくて、絶対パスでの指定ができるんじゃないかと!

WordPress のアドレス (URL)

の(URL)という表現が紛らわしいですよね。

※ちなみに最初の設定時ではなく、稼働中のものを途中で変更すると色々壊れてしまい、SQLで色々するハメになる場合がありますのでご注意下さい

MacOSX上でMacPortsを使わずにRails開発環境を作る

2010年03月04日
区分
MacPortsよりfink
報告者:
chiba

こんにちは、chibaです。
MacOSX上などにRails開発環境を複数作らなくてはいけなくて色々試行錯誤しています。
環境は、

  • デザイナーさん制作ツール: Coda
  • Ruby on Rails
  • Apache2
  • MacOSXだったり
  • Linuxだったり
  • 貧乏

という感じで、OS環境を2〜3種跨ぎ、かつ、MacOSXとWindowsは主にデザイナーさんが利用するプラットフォームです。

  1. Linuxなら直にRoR環境を構築してしまえば良いし、デザイナーさん達がLinuxは使うことはない
  2. MacOSXも直にMacPortsなどを利用してRoR環境を構築すれば良いけれど、デザイナーさん達はMacOSXをメインに使うらしいので、勝手に各自RoR環境を構築してみて下さいとも言いがたい
  3. 何か問題が起きた場合に、各自で環境を構築してもらうと、何がどうなってエラーになっているのか探るのが面倒臭い </ol> という状況でした。
    適当に考えた末、VirturlBox上にRoRの環境を構築し、そのVMイメージを皆で共有すれば楽チンで良いに違いない!ということで試しに、MacOSXの上にVirturlBoxを導入してみました。
    ホスト<=>VM間のファイル共有はNFS。
    本当は、VirturlBoxの共有ファイル機能を利用したかったのですが、なんだか上手く動かせなかったのでNFSに。
    ローカルのファイルをCodaで編集して、Apache/VirturlBoxがホストするページをローカルのブラウザで眺める、という感じで作業してもらうことになりました。
    MacOSXは、NFSなどの上で作業すると、._unkoや、.DS〜というファイルを大量にばらまきますが、この構成の場合、作業ディレクトリはホスト側なのでそういうこともありません。
    とりあえず、LinuxとMacOSXをホストにして実際に使ってみていますが、可もなく不可もないような、微妙な環境ができあがりました。
    もっと他にナイスな組み合わせを教えてください…。■

C-s/C-rを使おう

2010年02月26日
区分
Emacs
報告者:
chiba

chibaです。依然としてネタ切れです。
WordPressのことを調べようと思って前フリしてましたが、これまた全然進んでません。

ということなので、最近知ったEmacsの知ってる人には、当たり前な機能を紹介したいと思います。
Emacs使いの方は、C-sはもちろん良く利用すると思いますが、C-sで検索モードに入ってからの、C-w、M-yが便利であることを最近知りました。
C-wで単語単位で検索ワードを漸増しつつ検索できたりしますし、M-yは最新のキルリングの内容で検索できます。

また、C-sの応用として、カーソル移動に積極的にC-s/C-rを利用するのも非常に便利であることを再認識しています。
viだとf〜などが便利なのでfを良く使うのですが、EmacsでもC-sで似たようなことはできるんだなあと。
10年位使っていますが、この10年間だいぶ損をしていたなあと思った次第です。

真の豊かさとは何か

2010年02月18日
区分
生き甲斐
報告者:
chiba

こんにちは、chibaです。
今回は、WordPressのフォームネタの続きの筈なのですが、準備が全く間に合ってないので全然関係ない話を。
当社は、社内のコミュニケーションにskypeが活用されているのですが、Emacs派の私とtaharaさんは、もちろんskype.elを愛用しています。
複数人で会話しているので会話が埋もれがちになるのですが、これまでみんなに見ておいて欲しい作業の報告等には、目立つように絵文字をちりばめて派手にしていました。
しかし、それもだんだんスルーされるようになって来たことに危機感を抱いた私は、報告メッセージをAA化し出力することにし、それのためのelispをやっつけで書いてみました。

(defun omatase (arg title name mesg)
  (interactive "p\nstitle: \nsname: \nsmesg: ")
  (let* ((title-chars (append title nil))
         (last-1~3 (last title-chars 3))
         (butlast3 (butlast title-chars 3))
         (last3 (first last-1~3))
         (last2 (second last-1~3))
         (last1 (or (third last-1~3) 12288)))
    (insert (format "
.
┌○┐%s
│%c|ハハ  お待たせしました、%sさん
│%c|゚ω゚)
│%c| //
└○┘ (⌒)
  し⌒ 

%s

  ハハ  以上、よろしくお願いします
 (゚ω゚)
_(_つ/ ̄ ̄ ̄/_
  \/___/
"
                    (if (null butlast3)
                        ""
                      (apply 
                       #'concat 
                       (mapcar (lambda (c) 
                                 (format "\n│%c|" c))
                               butlast3)))
                    last3
                    name
                    last2
                    last1
                    mesg))))

M-x omataseで

title: 死んでもいやです
name: 名無し
mesg: そんなことできません

と入力すると、

.
┌○┐
│死|
│ん|
│で|
│も|
│い|
│や|ハハ  お待たせしました、名無しさん
│で|゚ω゚)
│す| //
└○┘ (⌒)
  し⌒ 

そんなことできません

  ハハ  以上、よろしくお願いします
 (゚ω゚)
_(_つ/ ̄ ̄ ̄/_
  \/___/

という風に出力されます。ただバッファにAAが出力されるだけですが、Emacsって良いですね。
メッセージは依然適当にスルーされています。 ■

低価格レンタルサーバーの共有SSLとWordPress (1)

2010年02月12日
区分
やすさ爆発
報告者:
chiba

こんにちは、chibaです。
完全にネタが切れてしまいましたので、普段に遭遇する困ったことを書いて行きたいと思います。
月額500円位からの低価格レンタルサーバーと、WordPressの組み合わせは、個人〜中小企業の強い味方なのではないかと思うのですが、SSLのフォームの設置で一癖あるらしい、と風の噂で聞きました。 具体的には、低価格レンタルサーバーでも共有SSLが追加オプションなしで無料で使えることが多いのですが、共有SSLなので、SSLでないURLとはだいぶ違ったURLになります。 例えば、さくらインターネットならば、

http://www.example.com

に対応する共有SSLのURLは、

https://securexxxx.sakura.ne.jp/example.com

になったり、Joe’sウェブホスティングならば、

https://serverxx.instantssl.co.jp/~example

であったり色々です。
どうやら、この辺りの事情とWordPressのデフォルトの挙動の相性が良くないらしいのですが、この辺りをだらだら探っていきたいと思います。
フォーム作成用のWordPressのプラグインは、Contact Form 7を利用することにし、

  • 1. 何が問題の原因なのかを理解する
  • 2. 先達の解決策を探る
  • 3. WordPressをいじれる位はPHPを勉強する
  • 4. WordPressが設置できる位の人なら簡単に設置/設定できるようなプラグインを作る
  • </ol> 位の流れで何回かに分けて書いてゆきたいと思います。
    果してPHPも知らない私にプラグインは作れるのでしょうか。■

括り出したい

2010年02月05日
区分
エコロジー
報告者:
chiba

こんにちは、千葉です! 完全なるネタ切れです。 以前のエントリーにも書きましたが、私は返り値をできるだけ使いたくなってしまう派です。 値を返すところは積極的に拾ってゆきたい。 そういう質なので、

if pred
  method(100)
else
  method(200)
end

のようなものを見ると

method(if pred
         100
       else
         200
       end)

と書きたくなってしまいます。ifは分岐が2つですが、Rubyのcase等の複数への分岐も同じくそうです。 LISPだと

(method (if pred 100 200))

という感じですが、このifの纏め方は、LISPではすんなり流れるものの、Rubyだとif〜endが重たくみえるので、なんとなく書いてはいけないようにさえ感じます。
条件演算子なら

method(pred ? 100 : 200)

という風にすっきりなのですが、なんとなく読みづらいですね。

PerlとC++にしかできないような気がした十五の夜

2010年01月28日
区分
CommonLISP愛
報告者:
chiba

こんにちは!、chibaです。 早くも書くネタが無いのですが、どうしたら良いんでしょうか。
今年の私のテーマは「挑戦」。 先日、そんな私にぴったりな、盗んだバイクで走り出したくなる挑戦をみつけました。

  • PerlとC++にしかできないような気がするアノ機能(挑戦者募集中)
  • #!/usr/bin/perl
    # PerlとC++は世界一。
    
    srand(time);
    
    my $a = 0;
    my $b = 0;
    for(my $i = 0; $i < 1000; $i++) {
        (rand(2)<1 ? $a : $b) += 1;
    }
    print "$a, $b\n"
    

    これをPerlや、C++以外の言語で書けるのか、と。
    私は、LISP野郎なのでLISPで回答するとして考えましたが「実行時に変数の名前によって任意の変数にアクセスし、その変数に値を代入できるか」という風にお題を解釈しました。 PerlやC++の場合は、?:にそういう機能があるのでしょう。 Common Lispだとどうなるかというと、「実行時に変数の名前によって任意の変数にアクセス」というのは、シンボル名による変数へのアクセスで可能なので、上をCommon Lispに訳すと

    (let ((a 0) (b 0))
      (declare (special a b))
      (dotimes (i 1000)
        (incf (symbol-value
               (if (< (random 2) 1) 'a 'b))))
      (format t "~A, ~A~%" a b))
    ;⇒ NIL
    ----------
    499, 501
    

    こんな感じに書くことになるのかなあと思いました。
    …というようなことをtaharaさんと昼食時に話していたのですが、taharaさん曰くifにsetfが付いてれば可能なんじゃないかとのこと。 なるほど、確かにそうです。
    setfというのは代入する構文のマクロなのですが、ユーザーが色々カスタマイズできます。 実行時に変数名でアクセス→代入というのではなく、そういう構文をマクロで定義するわけですね。
    ユーザーがifにsetfを書けば良いのですが、これが標準で付いている処理系をどっかでみたなーと思ったので調べてみるとCLISPがそうでした。 CLISPだと標準で

    (let ((a 0) (b 0))
      (dotimes (i 1000)
        (incf (if (< (random 2) 1) a b)))
      (format t "~A, ~A~%" a b))
    ;⇒ NIL
    ----------
    513, 487
    

    と書けます。自分はPerlは書けないのですが、+=をカスタマイズすることによって

    for(my $i = 0; $i < 1000; $i++) {
        if (rand(2) < 1) { 
          $a 
        } else {
          $b
        } += 1;
    }
    

    のようにも書けるようにユーザーが勝手に定義できる、という感じでしょうか。
    こんなことをしているとすぐカオスになりそうですが、LISPに構文は無いようなものなので特に混乱もなかったりします。LISPは単純で良いですね■

    Rubyの好きなところ

    2010年01月21日
    区分
    CommonLISP愛
    報告者:
    chiba

    こんにちは、chibaです! 私はCommon Lispからプログラミングを始めたせいか返り値を使ったプログラミングスタイルが好きなのですが、Rubyは多値と配列と可変長引数が統合されたようになっていて、なかなか使い勝手が良く、Rubyの好きなところの一つです。いまのところ一つです。

    x = 3
    [0, *if x.even?
           [1, 2, 3]
         else
           [:a, :b, :c]
         end]
    
    ;⇒ [0, :a, :b, :c]
    
    def iota(n)
      if n.zero?
        0
      else
        [n, *iota(n - 1)]
      end
    end
    
    iota(10)
    ;⇒ [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    
    Proc.new do |x, y| x * y;end [100,2]
    ;⇒ 200
    

    LISP/Schemeでも、書けるといえば書けるのですが、

    ;; Common LISP
    (let ((x 3))
      `#(0 ,@(if (evenp x)
                 '(1 2 3)
                 '(:a :b :c))))
    ;⇒ #(0 :A :B :C)
    
    (defun iota (n)
      (if (zerop n)
          (list 0)
          `(,n ,@(iota (1- n)))))
    
    ;⇒ (10 9 8 7 6 5 4 3 2 1 0)
    
    (multiple-value-call #'* (values-list '(100 2)))
    ;⇒ 200
    
    ;; もしくは
    (apply #'* '(100 2))
    ;⇒ 200
    

    Rubyの方が統一感があって良いですね。

    Geohash

    2010年01月14日
    区分
    疎外感
    報告者:
    chiba

    こんにちは、chibaです。 昨晩は、弊社技師同僚間のTwitterで、Geohashの話題が盛り上っておりました。 話に混ざれずに疎外感を感じつつ就寝しようとしたのですが、ふと、会話中で示されていたGeohashの説明のページを眺めたところ、仕組みが分かるようで、分からないような、微妙にもどかしい感じだったので、だらだらと説明のとおりに上から順にコードを書いてみることにしました。

  • Geohash - Wikipedia, the free encyclopedia
  • そんな感じで、説明の順番のとおりに書いてできたコードが下記です。 なんとなく仕組みは分かったけど、そういえば、エンコード手順の説明は書いてないんだなあ…、と自分勝手なことを考えながら就寝しました。

    (defpackage :geohash
      (:use :cl)
      (:import-from :shibuya.lisp
                    :$ :$*))
    
    (in-package :geohash)
    
    (defun tr (from to string)
      (map 'string 
           (lambda (c)
             (or (some (lambda (x y) 
                         (and (char-equal x c) y))
                       from to)
                 (char-upcase c)))
           string))
    
    (defun 5bits (string)
      (map 'list
           ($ format nil "~5,'0,B" 
              $ read-from-string 
              $ format nil "#32R~A" 
              $ tr "0123456789BCDEFGHJKMNPQRSTUVWXYZ" 
                   "0123456789ABCDEFGHKJKLMNOPQRSTUV" 
              $ string $)
           string))
    
    (defun longitude-latitude (strings)
      (let ((bit-c-list ($* concatenate 'list strings)))
        (loop :for c :in bit-c-list
              :for i :from (if (char= #\0 (car bit-c-list)) 0 1)
              :if (evenp i) :collect c :into even
              :else :collect c :into odd
              :finally (return 
                         (list :longitude
                               (coerce even 'string) 
                               :latitude
                               (coerce odd 'string))))))
    
    (defun bitlist (string)
      (map 'list ($ parse-integer $ string $) 
           string))
    
    (defun decode (bits min mid max)
      (if (endp bits)
          mid
          ($* decode 
              (cdr bits)
              (if (zerop (car bits))
                  (list min (/ (+ mid min) 2) mid)
                  (list mid (/ (+ mid max) 2) max)))))
    
    (defun decode-geohash (string)
      (let* ((u ($ longitude-latitude $ 5bits string))
             (lon (getf u :longitude))
             (lat (getf u :latitude)))
        (list 
         :longitude 
         ($ float $ decode (bitlist lon) -90 0 90)
         :latitude
         ($ float $ decode (bitlist lat) -180 0 180))))
    
    (decode-geohash "u4pruydqqvj")
    ;⇒ (:LONGITUDE 57.64911 :LATITUDE 10.407439)
    

    StumpWMは便利です

    2009年11月06日
    区分
    プログラムプレゼンテーション
    報告者:
    chiba

    今回のプログラム・プレゼンテーションのネタはStumpWMです。 StumpWMはタイル型ウィンドウマネージャーなのですが、Common LISPで色々と拡張することができるのが特長です。

    今回は、このちょっとしたカスタマイズネタや使い方です。

    Actindi社内では現在全面的にRedmineで仕事を管理していて、仕事の確認/依頼もチケット番号ですることが非常に多いです。
    いつもチケットの番号でやりとりするのですが、伝えられた番号をぱっと開きたいことが多いので、これをStumpWMで便利にしてみようということで工夫してみました

      
    (DEFPARAMETER *LAST-TICKET* NIL)
    
    (DEFCOMMAND SHOW-TICKET (TICKET-NUMBER) ((:STRING "Ticket#: "))
      (WHEN (RUN-SHELL-COMMAND 
             (FORMAT NIL 
                    "/usr/bin/firefox "https://redmine.example.com/issues/show/~A\""
                     TICKET-NUMBER))
       (SETQ *LAST-TICKET* TICKET-NUMBER)))
    
    (DEFCOMMAND EDIT-IT () ()
      (RUN-SHELL-COMMAND
        (FORMAT NIL
                "/usr/bin/firefox "https://redmine.example.com/issues/edit/~A\""
                *LAST-TICKET*)))
    

    非常にでっちあげ感の漂う適当なコードですが、show-ticket 1234という風に実行すると、Redmineの1234番のチケットがfirefoxで表示されます。
    edit-itコマンドは保存していた直前のチケット番号を編集状態で開くという単純なものです。 ちょっとした工夫ですが、割と便利で愛用しています。
    といった具合で、その他、StumpWMの機能の説明をして1時間をしのぎました。
    みなさんもご興味あれば、StumpWMをお試しあれ。

    さようならsed、こんにちはCommon Lisp

    2009年10月08日
    区分
    プログラムプレゼンテーション
    報告者:
    chiba

    chibaです。

    今回のプログラム・プレゼンテーションのネタは日常で使うちょっとしたスクリプトをCommon Lispでやってみようというものです。

    ネタ切れなので自分の ブログに書いたネタ を再演しつつ、ブログでは紹介してなかったwgetも併せて動かしてみました

  • 適当なwget
  • Software ToolsのごとくCommon Lisp上にユーティリティを作成して行けば、Common Lispだけでこういう細かい仕事はできるようになるやもしれません。

    ブログもどき的、あまりにもブログもどき的

    2009年10月08日
    区分
    プログラムプレゼンテーション
    報告者:
    chiba

    chibaです。

    今回のプログラム・プレゼンテーションのネタはこのブログもどきについてです。 そもそもは、ちょっと変ったことをしてみようということで、技術方面のブログはCLで作ってみようということになりました。

    構成は、

    • CL処理系: SBCL
    • AllegroServe(RubyでいうWEBrickのようなもの)
    という風になっています。

    SBCL処理系は、ライブラリをロードした状態の処理系のメモリイメージを実行可能ファイルとしてダンプできるのですが、適当に手元のノートPCでAllegroServe(CLのウェブサーバー)をロードした状態でダンプしたものを、さくらサーバーに置いたところ動いてしまったので、そのまま動かすことにしました。(x86の実行ファイルです)

    場当たり的に作っていく中で色々な問題に遭遇しました。

    • AllegroServeだとサーバーの処理系を大文字と小文字を区別するように設定することが前提になっているのでそうしないと色々困る。
    • AllegroServeをAllegro CL上で動かさない場合、日本語の扱いに難がある
    等々、割と根本的なところに問題があったりします。
    HUNCHENTOOT というサーバーもちょっと試てみたのですが、上記のような問題はないので、こちらに乗り換えたいところです

    Emacsでスクラッチファイルを日付順にして管理するライフハック

    2009年09月26日
    区分
    プログラムプレゼンテーション
    報告者:
    chiba

    chibaです。

    自分は特にプログラムを書くのが趣味という訳でもないので、プログラムプレゼンテーションは割と苦手なのですが、今回は、過去に作った便利Emacs Lispをプレゼンして場をしのぎました。

    アイデアとしては、ファイルを

    file-2009-09-26.txt
    のように命名規則を設けて作成するようにし(これもElispで生成するようにする)、その規則を元に前の日のファイルを開いたり、次の日のファイルを編集したりするのをスムースにする、というものです。
    gist: 13417 - GitHub
    この例では、前のファイルに移動するキーとしてsuper+p次のファイルがsuper+nという風に割り当てみています。
    単純なアイデアの割には便利ですので、お使いの環境でもっと洗練/発展させてみて下さい

    RSSは動いているか

    2009年08月12日
    区分
    blog制作
    報告者:
    chiba

    適当にRSSを作成しているのですが、適当すぎて上手く動いてない気がします。
    このエントリーがRSSリーダーに捕捉されれば、とりあえず良いのですが、されてない気がします…

    こわれたRSSを付けました

    2009年08月12日
    区分
    blog制作
    報告者:
    chiba

    がんばってRSSを付けたんですが、Safafiでは表示されません。
    どこかが壊れているようですが、Google Readerや、LDRではがんばって表示してくれるようです。 しかし、それも動きがちょっと変なようです。

    このブログの更新はSLIMEでしている!

    2009年08月11日
    区分
    blog制作
    報告者:
    chiba

    このブログの更新はSLIMEでしているんです!
    SLIME使ってる人には非常に優しいブログですね
    具体的には、このサーバーが動いているサーバーのSwankの4005番ポートをローカルにポートフォワードして、EmacsからSlimeで接続し、エントリーの関数を定義しています。簡単ですね!

    見た目だけはなんとか完成

    2009年08月10日
    区分
    blog制作
    報告者:
    chiba

    見た目だけは、machidaさんのcssを合わせることにより、なんとか完成しました。
    デザインされたhtmlをlmlに直したりしているのですが、これが割と大変です。
    lmlとは、htmlをS式で表現したものなのですが、簡潔でS式の編集環境が整ってさえいれば素のhtmlより簡単に編集できます。
    とはいえ、その編集環境を整えるのが面倒なのですが…。

    急激に重くなった様子

    2009年08月07日
    区分
    blog制作
    報告者:
    chiba

    画像はディレクトリごとにマッピングして読み込ませているのですが、いやに読み込みが遅い気がします。全部のファイルを個別にマッピングした方が良いのでしょうか。

    URLを毎度決めている

    2009年08月03日
    区分
    blog制作
    報告者:
    chiba

    今のところURLを毎回決めていますが、これに (get-universal-time) が使えることに気付きました。
    SLIME上で(get-universal-time)を評価してURLにすると良いかもと。 (get-universal-time)はUNIXタイムのように1970年からではなく、1900年からの秒数になります。

    CSSはウェブページの命

    2009年08月03日
    区分
    blog制作
    報告者:
    chiba

    駄目なページでもスタイルシートを当てるとそれなりに見えるのは不思議ですね。

    エントリーを後でどういう風に修正したら良いか

    2009年07月29日
    区分
    blog制作
    報告者:
    chiba

    エントリーを後でどういう風に修正したら良いか
    めんどくさそうです。

    Hunchentootにしたい

    2009年07月29日
    区分
    blog制作
    報告者:
    chiba

    色々面倒臭そうなのでHunchentootにしたくなってきました。 とはいえ、Hunchentootも使ったことがないのでなんとも言えません。

    テストエントリー

    2009年07月29日
    区分
    blog制作
    報告者:
    chiba

    SBCLだと日本語が通らないので、無理矢理にマクロを通したり、文字列をバイト列に変換したり涙ぐましいのですが、もっと簡単な方法はないんでしょうか。
    根本的に直さないと駄目な気がしてきました。 AllegroServeより良いのってどういうのがあるんでしょう。

    掃除は大事

    2009年04月10日
    区分
    システム管理
    報告者:
    chiba

    社内PCで突然電源がOFFになる不調なPCがあり、メモリチェック等色々対処してみたのですが、特に不具合は見付けられず、困っていました。
    稼働中のPCを触ってみたところかなり高温になっているという報告を受けたので、4月から新しい仲間になったmasuda氏が機転を効かせてPC内部を掃除したところ、ばっちりPCの温度が下がり、調子も良くなりました。
    どうも熱暴走でPCが落ちてしまっていたようです。
    基本ですが掃除は大事だなとあらためて思った一件です。

    こんにちは、こんにちは

    2009年03月09日
    区分
    報告者:
    chiba

    こんにちは、先週の金曜からお世話になっております、chiba☆2009と申します。 「改善」という言葉は自分の一番好きな言葉で座右の銘としております。 世界をCommon Lisp色に改善してゆきたいと思っています!
    どうぞよろしくお願いします!!

    技師部隊からの
    お知らせ

    【求人】エンジニア募集しています。

    本頁の来客数
    八十七万千百七十六名以上(計測停止中)

    メンバー一覧

    アクトインディ技師部隊員名簿

    アクトインディ技師部元隊員

    アクトインディへ

    カテゴリー

    アクトインディ

    aaaa