RSpec/Rails: uninitialized constant が出たら bin/spring stop を実行してみると吉

Rails 4 になってから RSpec で書かれたテストを実行した際に、uninitialized constant Admin のようなエラーが出ることがあります。Rails アプリケーションのソースコードAdmin というクラスなりモジュールなりは定義されているというのに。

この場合、とりあえず bin/spring stop を実行してみましょう。たぶん、一発で効き目があります。

この現象は、app ディレクトリに新たなサブディレクトリ(たとえば、sevices)を追加したときに発生します。

前回の bin/rspec コマンド実行時に読み込んだRailsアプリケーションがメモリ上に残っているため、app/services ディレクトリが自動読み込みの対象になりません。


実は、この現象に気付いたのは、拙著『実践Ruby on Rails 4 現場のプロから学ぶ本格Webプログラミング』の読者(複数)からの質問がきっかけでした。手順通り進めてもエラーが出てしまう、ということでした。

当初、私の環境ではうまく再現せず、その旨を返信すると、読者の方でもテストが通ったという回答が届くので、何らかの勘違いだろうと考えていたのですが、同様の質問が続くため、いろんな仮説を立てて検証してみたところ、エラーの再現と回避に成功しました。

RE:View の pdfmaker で日本語フォントを埋め込む

RE:View を使って PDF を作ってみたのですが、Ubuntu 上の Adobe Reader では日本語が文字化けして読めませんでした。たぶんフォントを埋め込む設定が必要なのだろうと思ったのですが、RE:View のドキュメント等には解説が載っていません。

悪戦苦闘の末、次のコマンドを実行することで解決。


$ kanji-config-updmap ipa

コマンドは一般ユーザーで実行します。ipa の部分に指定できる値は、/usr/local/texlive/2013/texmf-dist/fonts/map/dvipdfmx/jfontmaps ディレクトリで ls コマンドを実行すれば分かります。2013 の部分は適宜変更してください。

AWS/Ruby: OpenSSL::SSL::SSLError が出る問題への対処法(メモ)

Rails アプリケーションを Amazon Linux 上で動かしていて、OpenSSL::SSL::SSLError という例外に遭遇。open-uri の open メソッドで、外部の URL からファイルを取得するところで。

解決法は、http://d.hatena.ne.jp/komiyak/20130508/1367993536 および http://stackoverflow.com/a/5973759/513554にある通り。

簡単に言えば、(1) http://curl.haxx.se/ca/cacert.pem から証明書をダウンロードして適当なパス(例:/usr/local/etc/cacert.pem)に置き、(2) 環境変数 SSL_CERT_FILE にそのパスを指定して Rails アプリケーションを起動、すればOK。

起動スクリプトを /etc/init.d/ に置いているのなら、


export SSL_CERT_FILE=/usr/local/etc/cacert.pem

のような記述をそれに加える。

ubuntu: 2つ目の MongoDB を upstart service として追加する(作業メモ)

いま、Ubuntu Server 12.04において MongoDB が27017番ポートで動いています。これとは別のプロセスとして、27018番ポートでMongoDB をサービスとして動かすように設定します。


% sudo cp /etc/mongodb.conf /etc/mongodb.2nd.conf


% sudo vim /etc/mongodb.2nd.con

  • dbpath を /var/lib/mongodb.2nd に変更。
  • logpath を /var/log/mongodb.2nd/mongodb.log に変更。
  • port を 27018 に変更。


% sudo cp /etc/init/mongodb.conf /etc/init.mongodb.2nd.conf


% sudo vim /etc/mongodb.2nd.conf

  • pre-start script セクションの mongodb を mongodb.2nd に変更。
  • start-stop-daemon コマンドに --name mongodb.2nd オプションを追加。
  • start-stop-daemon コマンドの --config オプションの値を /etc/mongodb.2nd.conf に変更。


% sudo initctl reload-configuration
% sudo ln -s /lib/init/upstart-job /etc/init.d/mongodb.2nd
% sudo mkdir /var/lib/mongodb.2nd
% sudo chown mongodb:mongodb /var/lib/mongodb.2nd
% sudo mkdir /var/log/mongodb.2nd
% sudo chown mongodb:mongodb /var/log/mongodb.2nd
% sudo start mongodb.2nd

ubuntu: gedit の Ruby コード編集環境を整える

Ubuntu には gedit という(実は)結構使えるテキストエディタが標準で付いています。

しかし、デフォルトの状態では Ruby 1.9 で導入されたシンタックスにちゃんと色が付かなかったりします。

以下、環境を整える手順をご紹介します。

(1) apt-get で gedit-plugins をインストールします。


% sudo apt-get install gedit-plugins

(2) Gem パッケージ rubybuntu-gedit をインストールします。


% gem install rubybuntu-gedit
% rubybuntu-gedit install -3

質問が表示されるので S か h で答えていきます。S なら /usr/share 以下に、h なら ~/.local/share 以下にファイルがインストールされます。

(3) gedit の [編集]→[設定]メニューで各種設定を行います。

  • [エディター]タブを選択。「タブの幅」を2、「タブの代わりにスペースを挿入する」と「自動インデントを有効にする」にチェック。
  • [プラグイン]タブを選択。[Bracket Completion]、[Code comment]、[クイックオープン]にチェック。

他にも便利そうなプラグインがあるけれど、とりあえずここまでやれば、かなり改善されます。

[追記]

…と書いたけれど、gedit-gmate をインストールすればもっと簡単かな。 http://namakesugi.blog42.fc2.com/blog-entry-109.html をご覧ください。

Rails & MySQL: トランザクション分離レベルをグローバルに設定する

MySQL (InnoDB) のトランザクション分離レベルは、デフォルトで REPEATABLE READ です。この設定では、トランザクションの最初のクエリでデータベースのスナップショットを取ってしまうので、他のトランザクションがコミットした変更が見えません。Web アプリケーション開発では結構使いづらい分離レベルです。特に理由がないかぎり READ COMMITTED を採用したいところです。

Rails 3.x 時代までは、


ActiveRecord::Base.connection.
execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED')
ActiveRecord::Base.transaction do
# ...
end

のように書かなければなりませんでしたが、Rails 4 でトランザクションごとに分離レベルを指定できるようになりました:


ActiveRecord::Base.transaction(isolation: :read_committed) do
# ...
end

これは大変嬉しいことです。しかし、毎回こんな風に書くのは依然として面倒ですよね。それに、RSpec でテストをするときに config.use_transactional_fixtures = true という設定を採用していれば、こんなエラーに見舞われることでしょう:


ActiveRecord::TransactionIsolationError:
cannot set transaction isolation in a nested transaction

各エグザンプルは全体がトランザクションで囲まれているので、テスト中はアプリケーションの中でトランザクションの分離レベルを変更できないのです。

そこで、トランザクション分離レベルをグローバルに設定できないか、ということになります。

一番簡単なのは MySQL サーバのインスタンス全体でグローバルに変更することです:


mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

しかし、これを実行するには SUPER 権限が必要ですし、MySQL サーバを他のアプリケーションと共有している場合には、気軽に実施できません。

Rails アプリケーション単位でグローバルに分離レベルを設定するにはどうすればいいでしょうか。

すぐに思いつくのは、新規ファイル config/initializers/isolation_level.rb を作って、そこにこんな風に書いておくことです:


ActiveRecord::Base.connection.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')

これは開発環境ではうまく行きますが、本番環境では多分ダメです。というのは、データベースへの再接続が行われることがあるからです。例えば、Unicorn の workers を fork する時とか。

というわけで、私はモンキーパッチを当てることにしました:


class ActiveRecord::ConnectionAdapters::ConnectionPool
def new_connection_with_isolation_level
conn = new_connection_without_isolation_level
conn.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
conn
end
alias_method_chain :new_connection, :isolation_level
end

このコードを config/initializers/isolation_level.rb に書いておくわけです。

こういうことはあまりしたくないのですが、他に方法が思いつきませんでした。もし、ご存じの方がいらっしゃいましたら、是非教えてください。コメントでもメールでも結構です!

config/database.yml


production:
adapter: mysql2
encoding: utf8
transaction_isolation_level: read_committed
...

といった感じで設定できるといいのですけどね。

[訂正] この記事を最初に投稿したとき、私は次のようなモンキーパッチを紹介したのですが、実際には全然動きませんでした。


class ActiveRecord::Base
class << self
def establish_connection_with_transaction_isolation_level(*args)
establish_connection_without_transaction_isolation_level(*args)
connection.execute('SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED')
end

alias_method_chain :establish_connection, :transaction_isolation_level
end
end

テスト環境で動いたように見えたので、急いで記事を書いたのが敗因です。訂正します。(2013-11-22)

Raspberry Pi: Wi-Fi USB アダプタ(Wi-Fi ドングル)の選択

ここのところ、Raspberry Pi を使う仕事に関わっています。

Wi-Fi USB アダプタ(Wi-Fi ドングル)をいくつか買って試してみたところ、アダプタが熱を持つためか、しばらくするとネットワークが切れてしまう、という現象に悩まされました。

結局、長時間の使用に耐えたのは、PlanexのGW-USEco300のみ。

以下の製品は数分〜1時間で通信不能になりました。

  • BuffaloのWLI-UC-GNM
  • BuffaloのWLI-UC-G301N
  • I-O DATAのWN-G150UK
  • LogitecのLAN-W300N/U2
  • LogitecのLAN-W300N/U2SBK

あくまで私個人の経験に基づくものですが、PlanexのGW-USEco300は10個以上購入してすべて問題なかったので、かなり自信を持っておすすめできます。

(追記: 2013-10-16)

この記事を8月末に書いてから、いくつか新たな事実が判明したので、簡単に補足します:

  • 8月末に検証した際に使用していたのは、2013-07-26-wheezy-raspbian。
  • 2013-09-10-wheezy-raspbian にアップグレードすると状況が改善する場合がある。
  • おそらく赤外線受発信のためのモジュール lirc が原因で問題が発生する。
  • PlanexのGW-USEco300は製造中止で、大量入手は困難。

2013-09-10-wheezy-raspbian では、以下の製品が長時間の使用に耐えました。

  • I-O DATAのWN-G150UK
  • I-O DATAのWN-G300UK
  • LogitecのLAN-W150NU2AB
  • BUFFALOのWLI-UC-GNM

ただし、後半の2機種はかなり発熱するので、心配になります。