カテゴリ別 2003年 | 2004年 | 2005年 | 2006年 | 2007年 | 2008年
知り合いサイト: よんだもの / 暴想 / Linuxでやる夫 / 新宿Vipper / 僕だけが幸せになればいいのに。
るびま5号 を読んで rails を試してみました。で、さっそくはまってしまいました。既知の問題だとは思いますので、最初に ML でも調べればよかったのでしょうが……
コントローラの名前として既存の(インストール済みで利用可能な) Ruby のライブラリ名を指定すると、アクションの実行時にコントローラではなくライブラリがロードされてしまいます。
例えば、続・RubyOnRails を使ってみる で DebateController を作成し、scaffold を指定するところまで行ったとします。ここで、 続けて Profile という名前のコントローラを生成します。
$ ruby script/generate controller Profile
Debate と同様に scaffold を使います。
class ProfileController < ApplicationController
scaffold :topic
end
webrick を起動して http://localhost:3000/profile にアクセスします。
$ ruby script/server
=> Rails application started on http://127.0.0.1:3000
[2005-02-17 10:52:40] INFO WEBrick 1.3.1
[2005-02-17 10:52:40] INFO ruby 1.8.2 (2005-02-15) [i686-linux]
[2005-02-17 10:52:40] INFO WEBrick::HTTPServer#start: pid=14602 port=3000
% cumulative self self total
time seconds seconds calls ms/call ms/call name
1150.00 0.46 0.46 1 460.00 460.00 Profiler__.start_profile
0.00 0.46 0.00 1 0.00 0.00 Mutex#locked?
0.00 0.46 0.00 1 0.00 0.00 Array#each
0.00 0.46 0.00 2 0.00 0.00 String#==
0.00 0.46 0.00 1 0.00 0.00 WEBrick::HTTPServlet::AbstractServlet#service
0.00 0.46 0.00 1 0.00 0.00 Dispatcher#dispatch
0.00 0.46 0.00 1 0.00 40.00 #toplevel
0.00 0.46 0.00 2 0.00 0.00 Hash#[]
0.00 0.46 0.00 1 0.00 0.00 Dependencies.load?
0.00 0.46 0.00 1 0.00 0.00 Dependencies.mechanism
0.00 0.46 0.00 2 0.00 230.00 Dependencies.require_or_load
0.00 0.46 0.00 1 0.00 0.00 Errno::ENOTCONN#===
0.00 0.46 0.00 2 0.00 0.00 String#downcase
0.00 0.46 0.00 1 0.00 0.00 WEBrick::HTTPRequest#fixup
0.00 0.46 0.00 2 0.00 0.00 Hash#default
0.00 0.46 0.00 1 0.00 0.00 WEBrick::GenericServer#start
0.00 0.46 0.00 1 0.00 0.00 WEBrick::HTTPServer#run
0.00 0.46 0.00 1 0.00 0.00 WEBrick::HTTPRequest#body
0.00 0.46 0.00 1 0.00 0.00 Thread#current
0.00 0.46 0.00 4 0.00 0.00 Module#===
0.00 0.46 0.00 1 0.00 0.00 WEBrick::SimpleServer#start
0.00 0.46 0.00 1 0.00 0.00 DispatchServlet#handle_mapped
0.00 0.46 0.00 1 0.00 0.00 DispatchServlet#handle_dispatch
0.00 0.46 0.00 1 0.00 0.00 ActionController::Helpers::ClassMethods.inherited
0.00 0.46 0.00 1 0.00 0.00 ActionController::Dependencies::ClassMethods.inherited_without_helper
0.00 0.46 0.00 1 0.00 0.00 DispatchServlet#dispatch
0.00 0.46 0.00 1 0.00 0.00 String#empty?
0.00 0.46 0.00 1 0.00 0.00 DispatchServlet#do_GET
0.00 0.46 0.00 1 0.00 0.00 Enumerable.member?
0.00 0.46 0.00 1 0.00 0.00 WEBrick::HTTPServer#service
0.00 0.46 0.00 1 0.00 0.00 WEBrick::HTTPRequest#read_body
0.00 0.46 0.00 2 0.00 0.00 WEBrick::HTTPRequest#[]
0.00 0.46 0.00 2 0.00 0.00 Array#empty?
0.00 0.46 0.00 1 0.00 0.00 Exception#backtrace
0.00 0.46 0.00 2 0.00 0.00 Kernel.==
0.00 0.46 0.00 1 0.00 0.00 Object#const_missing
/usr/local/lib/ruby/1.8/profiler.rb:27: undefined method `[]' for nil:NilClass (NoMethodError)
from /usr/local/lib/ruby/1.8/profiler.rb:5:in `dispatch'
from script/server:49:in `dispatch'
from script/server:49
debug とか socket とかでも同様に変なことになります。コードも読まず、コントローラ名と実行結果との因果関係に気付くこともなく、「development 環境だから裏で profiler 動かしてパフォーマンスチューニングのデータを採っているんだろう。よく出来ているなあ」などと勝手に考えて、見当違いのところを調べていました……
環境は以下の通り。
ActiveRecord の勉強をするのに何か良い題材がないかと考えていましたが、それなりに複雑でそれなりに知っている MovableType のデータベースにします。
MT は mt-load.cgi でテーブル作成済み。
mt-load.cgi を叩くと初期ユーザが作成されますので、まずはそのデータを Ruby+ActiveRecord で表示するところから。
いきなりコードを書いてみます。
#!/usr/bin/env ruby
require 'rubygems'
require_gem 'activerecord'
require 'pp'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "localhost",
:username => "mt",
:password => "mt",
:database => "mt"
)
module MT
class Author < ActiveRecord::Base
end #Author
end #MT
pp MT::Author.find(1)
意図としては
となります。で、これを実行すると、
$ ruby sample.rb
/usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/abstract_adapter.rb:377:in `log': Table 'mt.authors' doesn't exist: SELECT * FROM authors WHERE id = 1 LIMIT 1 (ActiveRecord::StatementInvalid)
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:95:in `execute'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:173:in `select'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:73:in `select_all'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:337:in `find_by_sql'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:330:in `find_all'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:346:in `find_first'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:287:in `find'
from sample.rb:21
などと怒られます。エラーメッセージにも表示されていますが、debug.log に実行しようとした SQL が記録されますので、それを見てみます。
$ tail -1 debug.log MT::Author Load (0.000000) Table 'mt.authors' doesn't exist: SELECT * FROM authors WHERE id = 1 LIMIT 1
データベース mt には authors という名前のテーブルはないと言われています。これは ActiveRecord には、暗黙的なクラス名とテーブル名の命名規則があるためです。上のコードでは Author クラスを定義しましたので、authors テーブルとマッピングしようとします。
mt_author というテーブル名は変えられませんので、Ruby のコードの方で何とかしないといけません。何とかするには ActiveRecord::Base.table_name を使います。上のコードの Author クラス定義部分だけを抜き出してみます。
module MT
class Author < ActiveRecord::Base
def self.table_name
"mt_author"
end
end #Author
end #MT
再び実行してみるとまだエラーです。さっきとはエラーメッセージが違っていますね。
$ ruby sample.rb
/usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/abstract_adapter.rb:377:in `log': Unknown column 'id' in 'where clause': SELECT * FROM mt_author WHERE id = 1 LIMIT 1 (ActiveRecord::StatementInvalid)
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:95:in `execute'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:173:in `select'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/connection_adapters/mysql_adapter.rb:73:in `select_all'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:337:in `find_by_sql'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:330:in `find_all'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:346:in `find_first'
from /usr/lib/ruby/gems/1.8/gems/activerecord-1.7.0/lib/active_record/base.rb:287:in `find'
from sample.rb:24
$ tail -1 debug.log MT::Author Load (0.000000) Unknown column 'id' in 'where clause': SELECT * FROM mt_author WHERE id = 1 LIMIT 1
これは、ActiveRecord はデフォルトでは主キーのカラム名を id とみなすためです。mt_author のスキーマを見てみましょう。
mysql> show fields from mt_author; +-----------------------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------------------------+--------------+------+-----+---------+----------------+ | author_id | int(11) | | PRI | NULL | auto_increment | | author_name | varchar(50) | | MUL | | | | author_type | tinyint(4) | | | 0 | | | author_nickname | varchar(50) | YES | | NULL | | | author_password | varchar(60) | | | | | | author_email | varchar(75) | | MUL | | | | author_url | varchar(255) | YES | | NULL | | | author_can_create_blog | tinyint(4) | YES | | NULL | | | author_can_view_log | tinyint(4) | YES | | NULL | | | author_hint | varchar(75) | YES | | NULL | | | author_created_by | int(11) | YES | | NULL | | | author_public_key | text | YES | | NULL | | | author_preferred_language | varchar(50) | YES | | NULL | | | author_remote_auth_username | varchar(50) | YES | | NULL | | | author_remote_auth_token | varchar(50) | YES | | NULL | | +-----------------------------+--------------+------+-----+---------+----------------+ 15 rows in set (0.00 sec)
主キーは author_id ですね。これもデータベースの方は変更できませんから、Ruby+ActiveRecord 側でなんとかしなくてはなりません。で、なんとかするのがこのコードです。
ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
主キーの命名規則を指定します。author テーブルの主キーは author_id でしたので、「テーブル名_id」 という規則であるとしています。
これでまた実行すると、、、
$ ruby sample.rb
#<MT::Author:0x2af7c7a4
@attributes=
{"author_remote_auth_token"=>nil,
"author_name"=>"Melody",
"author_can_create_blog"=>"1",
"author_type"=>"1",
"author_nickname"=>nil,
"author_can_view_log"=>"1",
"author_created_by"=>nil,
"author_public_key"=>nil,
"author_id"=>"1",
"author_password"=>"0osHZ.scFVmok",
"author_email"=>"",
"author_remote_auth_username"=>nil,
"author_url"=>nil,
"author_hint"=>nil,
"author_preferred_language"=>"ja"}>
ようやく上手くいきました。
以上で見てきましたように、既に存在しているデータベースに対して ActiveRecord を使って OR マッピングをする場合には、ActiveRecord が想定するデフォルトの命名規則には従っていないことが多いため、定義やオプション設定をする必要があります。どんなパターンに対応できるかは、今のところ ActiveRecord の API で調べるしかないようです。
最終的なコード
#!/usr/bin/env ruby
require 'rubygems'
require_gem 'activerecord'
require 'pp'
require 'logger'
ActiveRecord::Base.logger = Logger.new("debug.log")
ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "localhost",
:username => "mt",
:password => "mt",
:database => "mt"
)
module MT
class Author < ActiveRecord::Base
def self.table_name
"mt_author"
end
end #Author
end #MT
pp MT::Author.find(1)
最近のコメント:
RSS
![]()
This work is licensed under a
Creative Commons License
(note: text only. w/o web design, citations, (re)distributed softwares).
_ キムキム [残念。。。もうこのページの意味分かりませんから そのページに来た私に切腹。。。]
_ MoonWolf [MTとActiveRecordの両方から同時にDBにアクセスするのでなければ、DBの構造を見直してActiveRec..]