MacにRMagickが入らなくてつらい人へ

大型淡水魚のピラルクーはとても特徴的な大きな鱗をもち、その硬さもあって、靴べらなどに利用されることさえあるのだそうです。

さて、Mac(El Capitan)にGemでRMagickを入れようとしたとき、

% gem install rmagick

こんなエラーで死にました。

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    /Users/hogehoge/.rbenv/versions/2.2.0/bin/ruby -r ./siteconf20170218-46131-rbo6q5.rb extconf.rb
checking for clang... yes
checking for Magick-config... no
checking for pkg-config... yes
checking for outdated ImageMagick version (<= 6.4.9)... no
checking for Ruby version >= 1.8.5... yes
checking for stdint.h... yes
checking for sys/types.h... yes
checking for wand/MagickWand.h... no

Can't install RMagick 2.15.4. Can't find MagickWand.h.
 *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/Users/hogehoge/.rbenv/versions/2.2.0/bin/$(RUBY_BASE_NAME)

extconf failed, exit code 1

Gem files will remain installed in /Users/hogehoge/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/rmagick-2.15.4 for inspection.
Results logged to /Users/hogehoge/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/extensions/x86_64-darwin-15/2.2.0-static/rmagick-2.15.4/gem_make.out

An error occurred while installing rmagick (2.15.4), and Bundler cannot continue.
Make sure that `gem install rmagick -v '2.15.4'` succeeds before bundling.

基本的にはこちらで書かれているような、pkg-config周りをちゃんとするようというようなのがよく引っかかります。
Mac OS X環境にrmagickをインストールする決定版 – Qiita

僕の場合はまた違う現象でした。
結論から言うと、新しいImageMagick7.xがシステムに入ってるとダメで、6.x系列を入れる必要がありました

% brew uninstall --ignore-dependencies --force imagemagick
% brew install imagemagick@6 && brew link imagemagick@6 --force
% gem install rmagick

この答えは、こちらのStackOverflowで発見しました。
ruby – RMagick installation: Can't find MagickWand.h – Stack Overflow

こういうので時間を溶かすのがいちばんつらいですね。

Mac+rbenvでconfigure: error: something wrong with LDFLAGS=”…”とか言われた時のいち解法

今日は多摩川花火大会をやっていまして、今まさに自室の窓の外から花火が見え音が聞こえる中こんなブログ記事を書いています。ダイナマイト漁で捕獲でもされている気分です。

さて、El Capitanでrbenvを使っていて、rbenv installをしようとしたときこんなかんじのエラーが出てちょっとハマりました。

% rbenv install 2.2.3
Downloading ruby-2.2.3.tar.bz2...
 -> https://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.3.tar.bz2
Installing ruby-2.2.3...

BUILD FAILED (OS X 10.11.6 using ruby-build 20160602-31-gf085feb)
...
checking whether LDFLAGS is valid... no
configure: error: something wrong with LDFLAGS="-L/Users/(myname)/.rbenv/versions/2.2.3/lib "
make: *** No targets specified and no makefile found.  Stop.

このエラーはいろんな状況で起こるようです。autoconfのログを見てもよくわかりませんでした。
ググってみると、やれrbenvプラグインのruby-buildが古いだの、やれ特定バージョンのrubyで起こるだの、gccを消せだの、brew doctorしろだの、手動ビルドしろだの…などなど、さまざまな原因でこの現象が起こるようですが、
僕の場合はLIBRARY_PATH環境変数に書いてあったディレクトリが実在しなかったことが原因でした。

つまり、

LIBRARY_PATH=/usr/local/lib:/usr/lib:/lib

だったのですが、僕の使用するEl Capitan機に/libというディレクトリは存在しなくて、それが結局rbenv installのエラーを引き起こしていました。
なので、環境変数から/libを除くか、/libをmkdirすると、rbenvのビルドは死ななくなりました。
エラーメッセージ自体からはそのことを直接は読み取れなかったので、ハマりました。

ご参考までに。

resque_specはGemfileのtest groupにのみ入れましょうねという雑なお話

季節は夏となりましたが気温の上下が激しく、体温調節が狂って風邪をひきやすそうな気候の今日このごろです。
普通、お魚は変温動物なので体温は周辺水温とほぼ全く同じ(代謝に伴うごく僅かな上昇程度)なのですが、サメの場合は5℃から15℃ほども高いのだそうです。マグロなどの高速で泳ぐ回遊魚にも同様の傾向が見られるのだそうです。そしてマグロの場合は、冷凍保存の質に影響するため比較的シビアな温度計測が求められるとのこと。
さて、Ruby on Rails (にかぎらずRuby)でのジョブキューイングにResqueを使っている際にハマったしょうもない出来事について、忘れないようにメモ。
結論から言うと、Gemfileではresque_specは:test groupだけに入れるようにしましょう、でないとジョブがキューに投入されないですよというだけの雑なお話です。

# Gemfile: Good
group :test do
  gem 'resque_spec'
end

ちゃんと、test環境のみでresque_specをアクティベートするように書きましょう。

# Gemfile: Bad!!!!!!
group :development, :test do
  gem 'resque_spec'
end
# or
gem 'resque_spec'
おこったこと

例えば上記の悪例のように、うっかり:developmentと:testにresque_specを書いてしまった場合。

Development環境(RAILS_ENV=development)でローカルPC上でresqueを走らせてジョブキューイングを実際に試そうとしています。
下記のようにちゃんとResqueを起動し、キューを監視してくれているというのに、

% QUEUE=default bundle exec rake resque:work
 ** [12:00:30 2016-06-07] 25304: Starting worker xxxx:25304:default
 ** [12:00:30 2016-06-07] 25304: Registered signals
 ** [12:00:30 2016-06-07] 25304: Running before_first_fork hooks
 ** [12:00:30 2016-06-07] 25304: Checking default
 ** [12:00:30 2016-06-07] 25304: Sleeping for 5.0 seconds
 ...

例えばコンソールからジョブをつっこんでも、

% rails c
> Resque.enqueue(HardWorker)

resqueサイドのほうが全く反応してくれず、queueingされていない様子…。
redisをのぞいても何かが入っている様子はない。
Resque.infoを見ても何も処理された様子がない。

というような事態になります。
なりました。
2時間ぐらい無駄にしました。つらい。

Gemを入れるとき、あまり深く考えずにdevelopmentとtest両方に入れちゃうことは皆さんままあるようなので(自分も含め)、全般的に少し注意したほうがいいポイントになりそうです。

ちなみに

じゃあ同じ論理で、rspecはテストに使うんだしrspecもtestの中だけに書いてあればいいよね!と思ったらそれも落とし穴だったりします。
rspec-rails は Gemfile で development グループにも入れてあげよう – blog.sorah

参考

Resqueのソース読んだり(結果的にはresque_specを見るべきであったので完全な無駄手間…)、めっちゃググったりしてやっと見つけたのがこちらのStackOverflow
redis – Rails development environment Resque.enqueue does not create jobs – Stack Overflow

resque_specのREADMEにも実はちゃんと書いてあるのですが、そもそもresque_specの不適切な設定が原因だと気づけなかったので、あとになって気づいた次第。あらためて、そもそも入れる時にちゃんと気をつけろという話。
leshill/resque_spec: RSpec matcher for Resque

やられたぜ!

嫁の顔忘れてもTimecop.returnは忘れないでねという話

夜釣りでは初心者向けの狙い目といえばアナゴなんだそうです。岸壁で釣れちゃうんだとか。ウナギはお高いのでアナゴで一杯、とかあるんだろうか。
さて、Rubyでいろいろ開発していて時間(時刻・期間…)が絡むテストをするときTimecopが便利で広く使われます。
travisjeffery/timecop – GitHub

# 現在時刻を変える
> Timecop.travel(Date.parse('1998/12/1'))
=> 1998-12-01 00:00:00 +0900
> Date.today
=> Tue, 01 Dec 1998
 
# 時間の進行を止める
> Timecop.freeze(Time.parse('1998/12/1 0:00:00'))
=> 1998-12-01 00:00:00 +0900
> sleep(10)
> Time.now
=> 1998-12-01 00:00:00 +0900  # it's still 0:00:00
 
# 時間の進行を早める
> Timecop.travel(Time.parse('1998/12/1 0:00:00'))
> Timecop.scale(3600)
> sleep(2)
> Time.now
=> 1998-12-01 02:00:19 +0900  # it took 2 hours although only slept for 2 seconds

便利ですね。
時間をいじくるので、

  • タイムゾーン周りをミスってないか
  • DBMSのタイムスタンプやredisのexpireのようにruby処理系の時間を使わないものでは効かない

こんなかんじで注意が必要なのは当然なのですが、一番しんどいのはTimecopで変えた時間を戻し忘れたときの挙動不審なかんじでしょうか。
テストでTimecopを使う場合、このテのミスをするとたいてい別のテストケースに影響しますね。順序依存で効いてくるし煽りを食ったテストケース自体は何も悪く無いことがほとんどなので、心神耗弱状態になります。

  • なぜかCapybaraのCookieが取れなくなっている
  • redisではうまくいくのにredis_mockでテストすると想定外の挙動になる
  • Timeoutが狂う

こんなことで時間をムダにするのはあほらしいので、Timecop.returnを呼ぶのは絶対に忘れないようにしましょう。

> Timecop.travel(Time.parse('1998/12/1 0:00:00'))
> Timecop.return
> Date.today
=> Sun, 20 Dec 2015

「忘れないでね」「気をつけてね」はものごとの解決策としては最悪な思考停止なので、
Rails/RSpecをお使いなら、spec/spec_helper.rbにて

config.after(:each) do
  Timecop.return
end

なる設定をしておくべきです。
また、別の安全策として、Timecopのsafe_modeといい、Timecopを使うときはblockを与えることを強制し、block末尾でreturnが自動で行われるようにする仕組みがあります。
blockを渡さずに呼ぶと例外が飛ぶので、すぐに気づきます。

> Timecop.safe_mode = true
> Timecop.travel(Time.parse('1998/12/1 0:00:00'))
Timecop::SafeModeException: Safe mode is enabled, only calls passing a block are allowed.
...
> Timecop.travel(Time.parse('1998/12/1 0:00:00')) do
>   Date.today
> end
=> Tue, 01 Dec 1998
> Date.today
=> Sun, 20 Dec 2015

Rails/RSpecのみで使うなら、spec_helperのafterでケアするのがシンプルにして安全十分かと。
テスト以外でも使うなら、safe_modeにするようinitializerなどで設定しておくほうが安全そうです。

全部Githubに書いてあるんですけども、1ヶ月近くこれにハマってしまった自分が本当にあほらしかったので忘れないように…。

matplotlibやskimageのimshowとかでウィンドウが全く表示されないならbackendを変えてみよう

サメはいわゆる魚類(分類学的には魚類というのは無いんだそうですね)に含まれますが、普通に「さかな」と言ってイメージするような魚を指す場合、「硬骨魚綱」と呼ぶとだいたい一致するんだそうです。サメやエイは「軟骨魚綱」になります。ただし、タツノオトシゴも硬骨魚綱に含まれます。

さて、matplotlibやskimageなどを使っていて、どんな描画結果をウィンドウに出そうとしてもでないとき。
X周りはちゃんとしているのに、例えばこんなコードを実行しても、ウィンドウが表示されない場合。

import skimage.io
img = skimage.io.imread('/path/to/image.png')
skimage.io.imshow(img)

Render backendを疑ってみてください。
matplotlibが何をベースに描画を行っているかにより、絵の質や出力方式や出力先が変わります。
現在のRender backendを調べてみましょう。

>>> print matplotlib.get_backend()
agg

無印のAGGは、画面への出力には対応していません。
このようなbackendsを、”noninteractive backends”と呼んでいます。

Here is a summary of the matplotlib renderers (there is an eponymous backed for each; these are non-interactive backends, capable of writing to a file):
Usage — Matplotlib 1.4.3 documentation

ということで、画面への出力にも使えるもの”interactive backends”をrender backendにしてしまいましょう。
backendの変更はスクリプト内でもできますが、ホントに最初にやらないといけないので面倒です。
そこで、デフォルト設定を変更してしまいます。

matplotlibの設定ファイルを探し出します。例えばpyenvを使っていたら下記のようなかんじで出力されます。

>>> matplotlib.matplotlib_fname()
u'/your/home/.pyenv/versions/2.7.9/lib/python2.7/site-packages/matplotlib-1.4.2-py2.7-linux-x86_64.egg/matplotlib/mpl-data/matplotlibrc'

で、このファイルを書き換えます。

 - backend      : agg
 + backend      : Qt4Agg

interactive backendであれば何でもOKです。Aggがついてるやつがおすすめです。
また依存ライブラリは事前に入れる必要があります。
例えばQt4Aggの場合はPyQtやPySideが、GTKAggの場合はPyGTKのインストールが必要です。

調べればすぐなんですけども、backendにはinteractiveとnoninteractiveがあり、デフォルトになっている無印AggがGUIをサポートしていないというのがハマりポイントでした。
pyenv環境にpipで入れたわけですが、配布しているパッケージによっては最初からinteractive backendがデフォルトになっていることもあるようです。