便利なライブラリが使えなかったのでrubyからsendmailコマンドを直接叩いて添付ファイル付きメール送信

ruby1.8.5しか入っていないサーバでメール送信するスクリプトを書くことになった。
rubyでメール送信するときはponyを使うと便利なので愛用していたのだけど、ruby1.8.5では最新版が動かない。シンタックスエラーが出まくる。*1
github.com

古いバージョンの中から動くものを探すのもいいかもしれないが、すごい手間が掛かりそう。依存ライブラリも含めてすべてのGemを調べる必要がある。
そんなことをするよりもsystem関数でsendmailコマンドを叩いてやったほうが早いかと思い、以下のスクリプトを書いた。
mime-typeがcsv固定になってたり汎用性は無い。


ほぼ、以下のシェルスクリプトの書き写し。qiita.com

まあ最新の環境が用意できるのが一番いいのだけど。そうもいかない場合もある。こういう基礎は大事だと思った。*2

*1:->記法とか{key:val}記法とか

*2:とはいえ最新のrubyの便利さも思い知らされた。温故知新。文字コードや改行文字の自動変換など本当にうまくやってくれる

WordPressのWordPressアドレスとサイトアドレスを別にすると管理バーが出なくなる。(あるいはWordPressのWordPressアドレスとサイトアドレスを別にしても百害あって一利なしということはもっと拡散されるべき)

百害というか一害なんですけども。field-archive.com
まったくもう。
管理バーが表示されないんですよ。
他の人が作ったサイトを引き継いで、ファイルやらDBやらをまとめて別サーバに移動させ、動作確認してみるとログインしても管理バーが表示されない。

ぐぐってもwp_footer()呼んでないんじゃないのとか、ユーザごとの表示設定間違ってんじゃないのとかログアウトしてログインしなおしたら直るとかそんな情報が大量に出てくる。それくらいもう確認しとるんじゃとやり場のない怒りを抑えつつ検索結果の下位のほうを一つ一つ確認してみたところ上記の記事が見つかりましたよ。この方神ですか。
これを解決するためにいろいろな設定を変更しては試し、を何回も何回も丹念に繰り返したことでしょう。
素晴らしいじゃないですか。
それに比べて「wp_footer()呼べば直りますよ?」とドヤ顔で書いているブログの浅ましさ(すみません言い過ぎです)。

まあそれは置いといて、WordPressWordPressアドレスとサイトアドレスが違っていると(先頭www.の有り無しとか。今回の俺のケースはこれでした。)こんな思わぬところで落とし穴にハマってしまうのでやめといたほうがいいよ。という話でした。
というか設定が2つある理由をよく理解しておくべきなんかな。調べてないけど。

さくらのレンタルサーバに入っているVimでもUTF-8使えるやん。(export LANG=ja_JP.UTF-8 だけで十分だった話)

さくらのレンタルサーバスタンダードプランにSSHログインしてVimを使おうとしたがUTF8なテキストが文字化けしてどうにもよろしくない。
手元のMacに入れてる.vimrcをコピーしてもダメ。
これはイカンとぐぐってみるとさくらのレンタルサーバVimはUTF8が化けるので自分でコンパイルしたという話がぞろぞろ出てきた。
そこで俺もと最新のソースを取ってきてやってみるがそれでもうまくいかない。
どうしたらいいのよ、と頭を抱えたところでターミナルの文字コード設定をEUCにしてみると綺麗に表示できる。
これはサーバのシステムの文字コードEUCになってるんだなと。
ということは、LANG環境変数か。で、export LANG=ja_JP.UTF-8を.bashrcに書いて、ターミナルはUTF8にしてみる。うまくいく。
もしや、これは、と思って、最初から入っているVimでUTF8のテキストを開いたらちゃんと読めるじゃないの。
てなわけでVimコンパイルとかは全く無駄でした。今のさくらのレンタルサーバに入っているVimはちゃんとUTF8が読み書きできるみたいです。
ネットの情報に振り回されて2時間無駄にしました。いやはや。

続・Windowsがシャットダウンするとき、動作中のrubyプログラムはどうなるのか? あるいはシグナルを他プロセスに投げられるとか甘えだし〜

昨日書いた記事の続編。
Windowsがシャットダウンするとき、動作中のrubyプログラムはどうなるのか? - koki-h's diary

Windowsでシグナルってどんなふうに扱われてるのかな、と思ってググってたら以下の記事を見つけた。

そもそも他プロセスにシグナル投げられるとかそんなの甘えだしー。
あ、他プロセスにシグナル投げられるかどうか、ってのはちょっと補足が必要か。
Unixの世界だとシグナルってOSの機能(っていうのかな)の一部だけど、他の世界では単に規格Cに定められた非同期例外処理である。
そして規格Cでは別のプロセスにシグナルを投げることができるなんて一言も言ってないし、僕らのMSVCRTでももちろん自プロセス内にしかシグナルは投げられない(raise(3)関数を参照せよ)。

http://www.garbagecollect.jp/~usa/d/201305a.html#id20130508_P1_4

衝撃の事実。Windows(というかUnix以外)のプロセスは外部からのシグナルを受け取れない。しかし自分の親プロセスからは受け取れる。
なるほど。コマンドプロンプトでCtrl+Cを押した時だけシグナルが捕捉できたのはそのためか‥。
そして俺の期待は甘えだったということが明らかになった。反省しま〜す。(納得いかない表情で)

Windowsがシャットダウンするとき、動作中のrubyプログラムはどうなるのか?

まあ強制終了されるのだけど。
普通、rubyプログラムは外部から強制終了させられるとき(INTシグナルが送られてきた時)はInterrupt例外を吐いて死ぬのだけど、OS自体がシャットダウンするときはどうなるのか?今回はWindows7で確かめてみた。ちなみにRubyruby 2.1.5p273 [i386-mingw32]というのを使った。
こんなプログラムを書いて実験してみた。

  • loop1.rb
while(true) do
  puts "working!"
  sleep 1
end

これを普通にCtrl+Cで強制終了するとまあ、以下のようになる。これは当たり前。

> ruby loop1.rb
working!
working!
working!
loop1.rb:3:in `sleep': Interrupt
  from loop2.rb:3:in `<main>'

今度は実行中にWindowsをシャットダウンしてみる。
シャットダウンした後にもう一度Windowsを立ち上げて実行結果を確かめられるように、標準出力と標準エラー出力をファイルに出すことにした。

> ruby loop1.rb > AAA.TXT 2> BBB.TXT

結果はAAA.TXTもBBB.TXTも空白。なんだそりゃ。
なぜ何も出力されないかを考えてみる。

  1. INTシグナル以外のシグナルが送られて来たことにより強制終了したため、Interrupt例外が発生しなかった。(INT以外のシグナルでも発生するかもしれないけど)
  2. シャットダウンの時は何のシグナルも送ってこず、ただ強制終了させられた。
  3. 何かしらのシグナルが送られてきたが、ruby側で捕捉する間もなく強制終了させられた。

とりあえずシャットダウンの時にシグナルが送られてきて、捕捉されるかどうかだけでも確かめたい。
以下の様なプログラムにしてみた。

  • loop2.rb
trap(:INT) { p "INT";exit }
#trap(:ILL) { p "ILL" }
#trap(:FPE) { p "FPE" }
#trap(:SEGV) { p "SEGV" }
trap(:TERM) { p "TERM";exit }
#trap(:BREAK){ p "BREAK" }
trap(:ABRT) { p "ABRT";exit }
trap(:EXIT) { p "EXIT";exit }

while(true) do
  puts "working!"
  sleep 1
end

リファレンスによれば、WindowsrubyではINT ILL FPE SEGV TERM BREAK ABRT EXITのシグナルが捕捉できる。
http://docs.ruby-lang.org/ja/2.1.0/class/Signal.html
ただし、ILL FPE SEGV BREAKはtrapメソッドの引数に指定すると実行時エラーになるのでコメントにした。
普通に実行してCtrl+Cで強制終了した場合*1は以下の様な出力になる。

> ruby loop2.rb
working!
working!
working!
"INT"
"EXIT"

*2
じゃあさっきと同じようにしてプログラムを起動し、Windowsをシャットダウンしてみる。

> ruby loop2.rb > AAA.TXT 2> BBB.TXT

そして、Windowsを立ち上げてAAA.TXTとBBB.TXTをテキストエディタで開いてみると。。
結果は空白。何にも出力されていなかった。

てなわけでrubyプログラムの実行中にWindowsをシャットダウンした時の動作は、

  • 何のシグナルも送ってこず、ただ強制終了させられる。
  • 何かしらのシグナルが送られるが、ruby側で捕捉する間もなく強制終了させられる。

の2つが濃厚なようです。あと、rubyで捕捉できないシグナルが送られてくる、っていうのも考えられるか。。

追記:2015/3/15
タスクマネージャーから強制終了したらどうなるのか?
気になったので試してみたらシャットダウンの時と同じ動きでした。



以下余談
こんなことを考えたのは、エラーログをなにも吐かずに異常終了したプログラム*3があって、なんとか終了した原因を知りたいというのがありました。やっぱり実行中にPC再起動しちゃったんじゃないかなあ。。WindowsUpdateとかだってあるかもしれんし。という疑いはあるのですが、証拠はありません。
それならOSのシャットダウンをRuby側で捕捉できるようにすれば、動かぬ証拠を記録できるのでは?と考えたのです。しかし結局ダメだった。

*1:本当は強制終了ではなくシグナルを捕捉した上で正常終了する。

*2:最後に”EXIT”が出ているのはINTを捕捉した後、exitメソッドを呼んだことによりEXITシグナルが送られてきたため。

*3:処理が終わるのにすっごく時間がかかる

sinatraとmechanizeを一緒に使うと動かない(動かす方法が悪いので)

sinatraで今WEBアプリを作っている。
WEBアクセスがあったらMechanizeで別のサイトへデータを取りに行く、みたいなの。

require 'sinatra'
require 'mechanize'

get "/" do
  agent = Mechanize.new
  agent.open("http://example.com")
 #とかなんとか
end

で、これを起動しようとするとエラーになる

$ bundle exec ruby web_main.rb
(省略)/gem/ruby/2.1.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1497:in `start_server': undefined method `run' for HTTP:Module (NoMethodError)
	from (省略)/gem/ruby/2.1.0/gems/sinatra-1.4.5/lib/sinatra/base.rb:1435:in `run!'
	from (省略)/gem/ruby/2.1.0/gems/sinatra-contrib-1.4.2/lib/sinatra/reloader.rb:252:in `run!'
	from (省略)/gem/ruby/2.1.0/gems/sinatra-1.4.5/lib/sinatra/main.rb:25:in `block in <module:Sinatra>'

メソッド名かなんかが競合しているようだ。

解決方法はStackOverflowに。
ruby - Mechanize & Sinatra conflict - Stack Overflow
まとめると、以下のようにすれば良い。

  • 手段その1. WEBハンドラを最上位に書くんじゃなくてクラスでくるんでやる
class MyApp < Sinatra::Base
  get '/' do
    # mechanize stuff
  end
end
  • 手段その2. Sinatraアプリを動かすサーバをWebrickからThinにする。(やり方はわからんがなんか設定すればできた気がする)

まだ試してないが、なるほどなと思った。

追記:Thinを入れるだけで手段その2が実現できる。簡単なのでこっちでやることにした。

Ubuntu ServerのユーザーディレクトリでPHPが使えるようにする。

以下は2014/10/17にはてなダイアリーに投稿した内容だが、ブログトップに表示されないのではてなブログにも転載することにした。
微妙に修正もしている。(元記事:Ubuntu ServerのユーザーディレクトリでPHPが使えるようにする。 - 橋本幸樹の無愛想な日記
まあ自分用のメモなので転載する意味はそれほど無いような気もする。
以下本文。



Ubuntu 10.04.4 LTSだとphpとユーザディレクトリのモジュールを有効にしただけではユーザディレクトリにPHPスクリプトを置いても実行されずにスクリプトがダウンロードされてしまう。

以下の設定ファイルを編集し、apacheを再起動すると動いてくれる。

  • /etc/apache2/mods-available/php5.conf

編集前

<IfModule mod_php5.c>
    <FilesMatch "\.ph(p3?|tml)$">
  SetHandler application/x-httpd-php
    </FilesMatch>
    <FilesMatch "\.phps$">
  SetHandler application/x-httpd-php-source
    </FilesMatch>
    # To re-enable php in user directories comment the following lines
    # (from <IfModule ...> to </IfModule>.) Do NOT set it to On as it
    # prevents .htaccess files from disabling it.
    <IfModule mod_userdir.c>
        <Directory /home/*/public_html>
            php_admin_value engine Off
        </Directory>
    </IfModule>
</IfModule>
~                   

編集後

<IfModule mod_php5.c>
    <FilesMatch "\.ph(p3?|tml)$">
  SetHandler application/x-httpd-php
    </FilesMatch>
    <FilesMatch "\.phps$">
  SetHandler application/x-httpd-php-source
    </FilesMatch>
    # To re-enable php in user directories comment the following lines
    # (from <IfModule ...> to </IfModule>.) Do NOT set it to On as it
    # prevents .htaccess files from disabling it.
    #<IfModule mod_userdir.c>
    #    <Directory /home/*/public_html>
    #        php_admin_value engine Off
    #    </Directory>
    #</IfModule>
</IfModule>

つまり、"<IfModule mod_userdir.c\>"のブロックをコメントアウトする。
"php_admin_value engine Off"という命令によってユーザディレクトリでのPHPの実行が抑制されているようだが、"php_admin_value engine On"にしてもPHPスクリプトへのアクセス時にサーバーエラーになってしまい実行できない。ブロック全体をコメントアウトまたは削除する必要がある。(よく読むとこのファイルのコメントにそう書いてある。)