著作一覧 |
能楽堂の環境整備をしていて rake db:create がデータベースを作らないことに気づいた。
確かに、SQL Serverに対してtiny_tds経由のsqlserver_adapterを使うと rake db:create でデータベースができないことは気づいていたのだが、SQL Expressについては作れるつもりになっていた。
が、作れない。
何が起きているのか、調べようとすると、これが厄介だった。
まず、logディレクトリに何も吐かれない。
どうもRakeが食ってしまうみたいだ。
rake -d .. と打ち込むと、-dなんて知らないと文句をたれてアボートする。頼むから、それをRubyに与えてやってくれと思うが(rake.batを修正するという手はあるけど)、rakeにそういうことを望むのは高望みというやつだろう。
で、rake --help すると山ほどオプションは出てくるのだが、どれも役に立たない。-X は警告のサプレスをやめると書いてあるが、付けようが付けまいが変化しない。-vはverboseだというがどこがverboseなんだ? というわけで役に立たない。結局、アンドキュメンテッドな -w で例外のメッセージ(btはなし)が出力されることはわかった。
で、エラーになっているのが、active_recordのdatabases.rake だというのはつかめた。早速、printf(Rubyだからpだけど)入れまくるが、出てこないのだが、これが。
どうも、ある時点から標準入出力とエラーを置き換えているみたいだ。それもNUL:へ。
が、irbで実行するとちゃんと出力されることを見つけた。
irb ARGV[0] = 'db:create' require 'rake' Rake.application.run
とすると、すべてのprintfが出力される。
で、結局、sqlserver_adapterが、connectする時点で、database.ymlに書いてあるdatabaseをuseさせようとしてtiny_tdsでエラーになっているとわかった。そりゃ、これから作ろうとしているのだから、存在するはずがない。
で、しょうがないので、とりあえずtiny_tdsからデータベースに関するエラーが来たら、masterをuseするようなパッチを作った。で、テストしたらパッチを投げる。
が、テストすると、今度はデータベースの重複エラーとなる。はて?
で、さらに追っかけると、今度はdatabases.rakeの中で、mysqlとsqliteとpostgresqlだけを特別扱いにしていて、それ以外については単にestablish_cconnectionとconnectionを呼んでいるだけとわかる。存在しないデータベースを接続パラメータとして与えるということから、特殊ロジックが必要だということなのだろう。で、その場合はなんでもかんでも「既に存在している」というリテラルを出力しているのだ。なんといい加減な。(あと、Rakeのdbタスクが非常に気に食わないのが、adapterのloggerアトリビュートを設定しないことだ。何気なくログを書いていたらナルポになって驚いた(ということはなくて、irbを使わないとエラーが表面に出てこないから何が起きているのかわからなくて閉口した。でirbを使うことになるのだが)。
ならば、しょうがないので手作りrakeすることにした。
で作っていて、以前も作ったことがあることに気づく。
かんたんRuby on RailsでWebアプリケーション開発(arton)これだ。で、本棚から取り出して読み返したりして。これ書いたときは、db:createなんて存在しなかったから、mysql用にrakeタスクの書き方の説明を兼ねて示したのだった。このときはほとんど何も無かったので、アダプタに対して構成情報を自力で与えたりしていた。
で、今は回りが充実しているのでもう少しシンプルに書ける。
namespace :db do task :create_db => :load_config do exec_from_master :create_database end task :drop_db => :load_config do exec_from_master :drop_database end def exec_from_master(cmd) ActiveRecord::Base.configurations.each_value do |config| target = config['database'] config['database'] = 'master' ActiveRecord::Base.establish_connection(config) ActiveRecord::Base.connection.__send__(cmd, target) end end end
これをlib/tasks に入れれば良い。
ジェズイットを見習え |