この記事『「新技術の習得」は麻薬だ 』を読んだんですが、共感するところが多いですね。次々と有象無象の新しい技術が出てきて、まるで「よどみにうかぶうたかた」のようにボコボコと出現しては消えていくわけですが、追いかけまくっていればそれだけで気持よくなってしまうかもしれません。
実際、技術者同士で話をしていると、知識が膨大にあるというだけで表面上は優秀に見えたりするし、相手の知らないことを自分が知っているという状況の優越感は理解はできます。でもそれが実際の仕事につながるかと問われれば、かなり怪しいですね。
僕の考えとしては、「自分が実際に今使っている、あるいはこれから仕事で使う技術」について徹底的に掘り下げて追求し、その過程で関連する新技術があれば吸収していくべきだと思っています。
注意すべきは、新技術の吸収よりも、現在使っている技術の掘り下げのほうが先だということです。実は、実際に仕事で使っているにもかかわらず、深く知っている、あるいは深く知ろうとする人って意外にも少ないです。
もちろん「深く」ってのもやりだしたらキリがないし、無意味な領域まで言ってしまっては仕方ないです。例えばWebアプリケーションの開発が仕事なのに、CPUのアーキテクチャに詳しくなっても、ちょっとそれは深く掘りすぎて反対側に突き抜けてしまった感があります。
でも、例えば自分が使っているフレームワークのソースコードを読んでみるとか、自分が使っている言語の解説本に徹底的に目を通してみるとか、そういう類のことならば少なくとも新技術を追いまくるよりは余程確実に役に立つし、実力の向上につながると思うんですよね。
そうやって、今使っている技術を掘り下げる中で関連する新技術に遭遇したならば、そこで初めて新技術を吸収すればいいと思います。そうすれば、実際の仕事に直結する形で新技術を勉強することになり、より有意義な勉強になるはずです。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年5月30日金曜日
capybara-webkitからpoltergeist(phantomjs)に乗り換え
RubyによるWebアプリケーションの自動化テストに便利なCapybara。JavascriptによるDOM操作も、capybara-webkitドライバを経由してテストすることができます。当初はそれで良かったんですが、とある事情によりXサーバ関連ファイル群を入れないで動かしたい場合があったので、Xサーバ関連ファイル群を必要とする仮想フレームバッファ(xvfb)への依存を避けたくなりました。
Capybaraにはもう1つ、JavascriptによるDOM操作を含むテストを実行できる手段がありまして、それがPoltergeistドライバを経由してPhantomJSを動かす、というものです。PhantomJSは仮想ブラウザみたいなもので(乱暴な説明)、詳しい説明は他サイトに譲ります。CabybaraからPhantomJSを動かすためのドライバがPoltergeistです。
ということで、Capybara-webkitからPoltergeist(経由のPhantomJS)に乗り換えることにしました。
まず、PhantomJSをインストールします。僕の環境としてはDebian7ですが、公式サイトのビルド済みパッケージを取ってきたらそのまま動公式サイトにLinux用のビルド済みバイナリがあるので、入手してください。2014/05/29現在の最新版はver1.9.7であり、
"phantomjs-1.9.7-linux-x86_64.tar.bz2"というリンクからダウンロードできます。
解凍して出来たディレクトリの bin/phantomjs をPATHの通った適当な場所に移動します。
例)$HOME/bin に移動する場合
なお、phantomjsの動作には fontconfig, freetype ライブラリが必要ですが、最近のLinuxデスクトップ環境ならば、ほとんどの場合にはすでに入っているでしょう。
ruby側の対応は巷のサイトに書いてあるとおりでOKです。まずGemfileへPolgergeistの記述を追加します。
僕はCucumber経由でcapybaraを使ってますので、features/support/env.rb に必要ライブラリの require と default_driver, javascript_driverの変更を記述します。(Javascirpt関連テストの時だけ使いたい場合は、default_driver側の記述は不要です。)
なお、Cucumberのテスト自体は書き換えずに動作しました。まだそんなにテストの量がないせいか、速度の違いは感じられません。
PhantomJSは活発に開発されているようなので、今後も色々と改善が期待できますね。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
Capybaraにはもう1つ、JavascriptによるDOM操作を含むテストを実行できる手段がありまして、それがPoltergeistドライバを経由してPhantomJSを動かす、というものです。PhantomJSは仮想ブラウザみたいなもので(乱暴な説明)、詳しい説明は他サイトに譲ります。CabybaraからPhantomJSを動かすためのドライバがPoltergeistです。
ということで、Capybara-webkitからPoltergeist(経由のPhantomJS)に乗り換えることにしました。
まず、PhantomJSをインストールします。僕の環境としてはDebian7ですが、公式サイトのビルド済みパッケージを取ってきたらそのまま動公式サイトにLinux用のビルド済みバイナリがあるので、入手してください。2014/05/29現在の最新版はver1.9.7であり、
"phantomjs-1.9.7-linux-x86_64.tar.bz2"というリンクからダウンロードできます。
解凍して出来たディレクトリの bin/phantomjs をPATHの通った適当な場所に移動します。
例)$HOME/bin に移動する場合
[user@host]% tar jxvf phantomjs-1.9.7-linux-x86_64.tar.bz2 [user@host]% cp phantomjs-1.9.7-linux-x86_64/bin/phantomjs ~/bin
なお、phantomjsの動作には fontconfig, freetype ライブラリが必要ですが、最近のLinuxデスクトップ環境ならば、ほとんどの場合にはすでに入っているでしょう。
ruby側の対応は巷のサイトに書いてあるとおりでOKです。まずGemfileへPolgergeistの記述を追加します。
gem 'poltergeist'
僕はCucumber経由でcapybaraを使ってますので、features/support/env.rb に必要ライブラリの require と default_driver, javascript_driverの変更を記述します。(Javascirpt関連テストの時だけ使いたい場合は、default_driver側の記述は不要です。)
require 'capybara/poltergeist' Capybara.default_driver = :poltergeist Capybara.javascript_driver = :poltergeist
なお、Cucumberのテスト自体は書き換えずに動作しました。まだそんなにテストの量がないせいか、速度の違いは感じられません。
PhantomJSは活発に開発されているようなので、今後も色々と改善が期待できますね。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年5月29日木曜日
Padrinoの起動時のロード順・フック実行について
今回のテーマは実は、Padrinoにおいてconfig/boot.rbにはbefore_loadフックとafter_loadフックの登録を行うコード記述があるけれど、config/boot.rbファイルのトップレベルに直接書くのと before_loadやafter_loadの中に書くのではどう違うのか、という疑問が切っ掛けでした。
Padrinoが生成するconfig/boot.rbは末尾で Padrino.load! を呼び出していますが、実はその中でbefore_loadとafter_loadを呼んでいます。Padino.load!の定義は、ソースコードを調べると次のようになってます。
つまりbefore_loadとafter_loadの違いは、Padrinoの各種依存ファイルのrequireの前か後か、という違いです。
上記Padino.load!をよく見てみると、Loggerだけ扱いが別になっています。Logger関連の設定はbefore_loadやafter_loadの中に書いてもそれだけでは有効になりません。config/boot.rbのトップレベルかつPadrino.load! の前に記述するか、手動でLogger.setup!を呼ぶ必要があります。
で、ようやく本題ですが、上記のPadrino.load!を見ながら、実際フレームワーク全体がどう読み込まれるのか調べてみたくなりました。Loggerのログレベルをdevelに設定すると、起動時にロードしたファイルをログに出してくれるようになるので、ロードの順番がわかります。まとめるとPadrinoのロードの主要手順はだいたいこんな感じになっているようです。
なるほど。ちなみに意外だったのは、controllersとhelpersのロード順が予想と逆だったこと。でもどうせメソッド定義なので特にロード順に影響はないはずです。
ロードの順番に影響を受けるようなコードの書き方をすることは少ない(というかなるべく避けるべき)と思いますが、デバッグの役に立つこともありますので一応知っておくとよいと思われます。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
Padrinoが生成するconfig/boot.rbは末尾で Padrino.load! を呼び出していますが、実はその中でbefore_loadとafter_loadを呼んでいます。Padino.load!の定義は、ソースコードを調べると次のようになってます。
def load!
return false if loaded?
began_at = Time.now
@_called_from = first_caller
set_encoding
set_load_paths(*load_paths)
Logger.setup!
require_dependencies("#{root}/config/database.rb")
Reloader.lock!
before_load.each(&:call)
require_dependencies(*dependency_paths)
after_load.each(&:call)
logger.devel "Loaded Padrino in #{Time.now - began_at} seconds"
Thread.current[:padrino_loaded] = true
end
つまりbefore_loadとafter_loadの違いは、Padrinoの各種依存ファイルのrequireの前か後か、という違いです。
上記Padino.load!をよく見てみると、Loggerだけ扱いが別になっています。Logger関連の設定はbefore_loadやafter_loadの中に書いてもそれだけでは有効になりません。config/boot.rbのトップレベルかつPadrino.load! の前に記述するか、手動でLogger.setup!を呼ぶ必要があります。
で、ようやく本題ですが、上記のPadrino.load!を見ながら、実際フレームワーク全体がどう読み込まれるのか調べてみたくなりました。Loggerのログレベルをdevelに設定すると、起動時にロードしたファイルをログに出してくれるようになるので、ロードの順番がわかります。まとめるとPadrinoのロードの主要手順はだいたいこんな感じになっているようです。
- Loggerの設定
- config/database.rbのロード
- before_loadフックの実行
- lib/の中にあるファイルのロード
- models/の中にあるファイルのロード
- (shared/lib/の中にあるファイルのロード)※デフォルトでは存在しない
- (shared/models/の中にあるファイルのロード)※デフォルトでは存在しない
- config/apps.rbのロード
- app/app.rbのロード
- アプリケーション名::Appのインスタンス生成
- after_loadフックの実行
- app/controllers/の中にあるファイルのロード
- app/helpers/の中にあるファイルのロード
なるほど。ちなみに意外だったのは、controllersとhelpersのロード順が予想と逆だったこと。でもどうせメソッド定義なので特にロード順に影響はないはずです。
ロードの順番に影響を受けるようなコードの書き方をすることは少ない(というかなるべく避けるべき)と思いますが、デバッグの役に立つこともありますので一応知っておくとよいと思われます。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年5月28日水曜日
Padrinoのhelperで特定インスタンスを使いまわす
Padrinoではコントローラやビューから呼び出すことのできるメソッドをhelperメソッドとして定義することができます。
helperメソッドはリクエスト中に何度も呼ばれたりするので、例えばサーバへの接続を行う場合、何度も接続インスタンスをnewせずに使いまわしたい場合がありますよね。
実はある特定のインスタンスを使いまわす方法は単純で、helperメソッド内でインスタンス変数に入れておけばいいです。例えば、LDAPサーバへの接続を取得するヘルパーメソッドを書くとしたら、
みたいな感じになりますね。見ての通り、@ldap_connが未定義の場合は新規接続を作成してそのインスタンスを@ldap_connに代入するとともに返し、すでに@ldap_connが定義されていれば@ldap_connの中身を返します。
ちなみにこのインスタンス変数@ldap_connはどのインスタンスに属するかというと、MyApplication::Appのインスタンスに属することになります。これは get や post メソッド内で定義したインスタンス変数と同じ扱いです。テンプレートからも参照できることからわかるように、これらのインスタンス変数は同じリクエスト中は直接呼んで使いまわすことが出来ます。
今回はLDAP接続を例にしましたが、DBサーバやキャッシュサーバなどの各種サーバへの接続など生成処理の重いインスタンス全般に使えると思います。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
helperメソッドはリクエスト中に何度も呼ばれたりするので、例えばサーバへの接続を行う場合、何度も接続インスタンスをnewせずに使いまわしたい場合がありますよね。
実はある特定のインスタンスを使いまわす方法は単純で、helperメソッド内でインスタンス変数に入れておけばいいです。例えば、LDAPサーバへの接続を取得するヘルパーメソッドを書くとしたら、
MyApplication::App.helpers do
def ldap_conn
@ldap_conn ||= LDAP::Conn.new('localhost')
end
end
みたいな感じになりますね。見ての通り、@ldap_connが未定義の場合は新規接続を作成してそのインスタンスを@ldap_connに代入するとともに返し、すでに@ldap_connが定義されていれば@ldap_connの中身を返します。
ちなみにこのインスタンス変数@ldap_connはどのインスタンスに属するかというと、MyApplication::Appのインスタンスに属することになります。これは get や post メソッド内で定義したインスタンス変数と同じ扱いです。テンプレートからも参照できることからわかるように、これらのインスタンス変数は同じリクエスト中は直接呼んで使いまわすことが出来ます。
今回はLDAP接続を例にしましたが、DBサーバやキャッシュサーバなどの各種サーバへの接続など生成処理の重いインスタンス全般に使えると思います。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年5月27日火曜日
Redis中のRackセッションのデータを直接見る
RubyのフレームワークPadrinoで開発しているアプリケーションのセッション管理をRack::Session::Redisでやることにしたわけですけれども、検証作業中にRedisに保存したセッションのデータをRack::Session::Redisを介さずに直接見てみたくなりました。
というわけで、Redisに格納されているセッションのデータを直接見る方法です。
セッションIDが分かっていることが条件です。RackアプリケーションでのセッションID取得方法はこちらを参照。
irbからRedisクラスを直接叩いて見てみることにします(以下、見やすいように改行を加えてあります)。
という感じで直接見ることができます。
Rackアプリケーションのセッションデータは、デフォルト設定では rack:session という名前空間の下に収められています。この名前空間は変更可能ですがあまり変更することはないでしょう。
データはMarshal.#dumpでシリアライズされているようで、読みだす時はMarshal.#loadを使えばよいです。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
というわけで、Redisに格納されているセッションのデータを直接見る方法です。
セッションIDが分かっていることが条件です。RackアプリケーションでのセッションID取得方法はこちらを参照。
irbからRedisクラスを直接叩いて見てみることにします(以下、見やすいように改行を加えてあります)。
[user@host]% bundle exec irb
irb(main):001:0> require 'redis'
=> true
irb(main):002:0> redis = Redis.new(:url => 'redis://127.0.0.1:6379/0')
=> #<Redis client v3.0.7 for redis://127.0.0.1:6379/0>
irb(main):003:0> Marshal.load(redis.get('rack:session:セッションID'))
=> セッションのデータ
という感じで直接見ることができます。
Rackアプリケーションのセッションデータは、デフォルト設定では rack:session という名前空間の下に収められています。この名前空間は変更可能ですがあまり変更することはないでしょう。
データはMarshal.#dumpでシリアライズされているようで、読みだす時はMarshal.#loadを使えばよいです。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年5月24日土曜日
やっぱりセッション管理はRack::Session::Redisで
昨日Rack::Session::Dalliを使うって言った舌の根も乾かぬうちに別のことを言い始めたよコイツ、とのツッコミを受けそうです。すいません。いや実は職場の人にRack::Session::Redisってのもあるよ、とアドバイスを頂きまして、調べてみたらRedisが結構良さそうだし面白そうなのでRedis使いたくなってしまったのですよ。
ちなみに、Rack::Session::Redisについて調べてみると、このクラスを提供しているライブラリが2つ見つかります。rack-session-redis と redis-rack の2つですね。
redis-rackのほうが新しいです。ソースコードも読んでみましたが、redis-rackのほうがよく整理されている印象を受けました。redisにデータを保存するための抽象化レイヤが redis-store として分離されていて、redis-rackはその上に乗っている構造のようです。
ということで、redis-rackを導入します。以下、Padrinoフレームワークを使っている前提です。
Gemfileに以下の記述を加えます。
app/app.rbに以下の記述を加えます。
セッションに使うライブラリを変更するため disable :sessions とするのですが、:protection と :protect_from_csrf は enable:sessions を前提にしておりエラーになるため事前に falseに設定し、その後手動で該当するミドルウェアである Rack::Protection と Rack::Protection::AuthenticityToken を use します。
なお、Redisの接続先はRack::Session::Redisのデフォルトのオプションで
と定義されてます。特に変更の必要はないので今回はサーバを指定していません。オプションはセッションの有効期限だけを指定しています(上の例だと30日を秒数で指定)。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
ちなみに、Rack::Session::Redisについて調べてみると、このクラスを提供しているライブラリが2つ見つかります。rack-session-redis と redis-rack の2つですね。
redis-rackのほうが新しいです。ソースコードも読んでみましたが、redis-rackのほうがよく整理されている印象を受けました。redisにデータを保存するための抽象化レイヤが redis-store として分離されていて、redis-rackはその上に乗っている構造のようです。
ということで、redis-rackを導入します。以下、Padrinoフレームワークを使っている前提です。
Gemfileに以下の記述を加えます。
gem ‘redis-rack’
app/app.rbに以下の記述を加えます。
set :protection, false set :protect_from_csrf, false disable :sessions use Rack::Session::Redis, :expire_after => 30 * 24 * 60 * 60 use Rack::Protection use Rack::Protection::AuthenticityToken, :authenticity_param => '_csrf_token'
セッションに使うライブラリを変更するため disable :sessions とするのですが、:protection と :protect_from_csrf は enable:sessions を前提にしておりエラーになるため事前に falseに設定し、その後手動で該当するミドルウェアである Rack::Protection と Rack::Protection::AuthenticityToken を use します。
なお、Redisの接続先はRack::Session::Redisのデフォルトのオプションで
:redis_server => 'redis://127.0.0.1:6379/0/rack:session'
と定義されてます。特に変更の必要はないので今回はサーバを指定していません。オプションはセッションの有効期限だけを指定しています(上の例だと30日を秒数で指定)。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年5月23日金曜日
セッション管理は Rack::Session::Dalli で
昨日書いたように、Rack::Session::Pool使用中にセッションが破棄される という問題が発生してまして、その打開策として最初 Rack::Session::Memcache への移行を検討してました。
Rack::Session::Memcache はバックエンドに memcache-client パッケージを使うのですが、実は memcache-client のメンテを担当している開発者によって memcache-client の後継である dalli というパッケージが出ていました。
ここで、memcache-client と dalli という2つのパッケージが登場したので、他にもあるかと一応調べたら memdached という名前のパッケージもありますね(直球な名前だな)。これはCで書かれたAPIをRubyから呼ぶもので、速さだけで言ったら最速みたいですけれども、Rackに組み込む簡単な方法はパッと見つかりませんでした。やるとしたらRack::Session::Memcache を書き換えるのが一番現実的でしょうけど、あまりやりたくありません。
一方、dalli には Rack::Session::Dalli クラスが付属していて、これを使うと簡単に導入できるみたいでした。これは魅力的。さすがにdalliはPure Ruby実装なので速度面で劣るものの、それでもパフォーマンスは結構良好なようですので大丈夫でしょう。今回はそこまで速度重視の目的でもないですし。
ということで、 Rack::Session::Dalli を導入します。Padrinoフレームワークを前提に説明しますので、適宜読み替えてください。
Gemfileに
と加えます。そして、config/boot.rb に
を追記します(Bundler.require では自動的にロードされないためです)。
そして、app/app.rb に
って感じに設定すればOKです。サーバはデフォルトで 'localhost:11211’ になってますが、IPアドレスを打たないときちんと認識しなかったので明示的に '127.0.0.1:11211’ を設定しています。
参考1→ http://stackoverflow.com/questions/12786976/how-to-specify-memcache-server-to-racksessionmemcache/12787492#12787492
参考2 →http://www.banana-systems.com/2010/09/rails3_memcached/
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
Rack::Session::Memcache はバックエンドに memcache-client パッケージを使うのですが、実は memcache-client のメンテを担当している開発者によって memcache-client の後継である dalli というパッケージが出ていました。
ここで、memcache-client と dalli という2つのパッケージが登場したので、他にもあるかと一応調べたら memdached という名前のパッケージもありますね(直球な名前だな)。これはCで書かれたAPIをRubyから呼ぶもので、速さだけで言ったら最速みたいですけれども、Rackに組み込む簡単な方法はパッと見つかりませんでした。やるとしたらRack::Session::Memcache を書き換えるのが一番現実的でしょうけど、あまりやりたくありません。
一方、dalli には Rack::Session::Dalli クラスが付属していて、これを使うと簡単に導入できるみたいでした。これは魅力的。さすがにdalliはPure Ruby実装なので速度面で劣るものの、それでもパフォーマンスは結構良好なようですので大丈夫でしょう。今回はそこまで速度重視の目的でもないですし。
ということで、 Rack::Session::Dalli を導入します。Padrinoフレームワークを前提に説明しますので、適宜読み替えてください。
Gemfileに
gem ‘dalli’
と加えます。そして、config/boot.rb に
require 'rack/session/dalli’
を追記します(Bundler.require では自動的にロードされないためです)。
そして、app/app.rb に
use Rack::Session::Dalli, :expire_after => nil, :memcache_server => '127.0.0.1:11211’
って感じに設定すればOKです。サーバはデフォルトで 'localhost:11211’ になってますが、IPアドレスを打たないときちんと認識しなかったので明示的に '127.0.0.1:11211’ を設定しています。
参考1→ http://stackoverflow.com/questions/12786976/how-to-specify-memcache-server-to-racksessionmemcache/12787492#12787492
参考2 →http://www.banana-systems.com/2010/09/rails3_memcached/
登録:
投稿 (Atom)