2007年12月08日

Ruby on Rails 2.0

ついにRails 2.0がリリースになったようです。
あぁ、時間が欲しいなぁ。そうしたら今読ミを2.0化するのに。


昨日書いたiTunes Music Storeアクセス不能は直ってました。

投稿者 iwazawa : 02:01 | コメント (0)

2007年11月10日

Migration中にデータをあれこれ

先日の条件付きテーブル間リレーションシップの続きであります。

Migrationはこんな感じか。

class CreateComments < ActiveRecord::Migration def self.up create_table :comments do |t| t.column :book_id, :integer t.column :content_type, :integer t.column :content, :text end Comment.reset_column_information Book.find(:all, :conditions=>["comment is not null AND comment != ''"]).each do |book| cmnt = Comment.create(:book_id=>book.id, :content_type=>1, :content=>book.comment) end remove_column :books, :comment add_index :comments, [:books_id, :content_type] end def self.down add_column :books, :comment, :text Book.reset_column_information Comment.find(:all, :conditions=>["content_type=1"]).each do |cmnt| book = Book.find(cmnt.book_id) book.comment = cmnt.content book.update end drop_table :comments end end

これで、self.up 時に、books テーブルに格納されていたコメントが、新しい comments テーブルに移行できる。self.down はその逆である。

ここで少し、道草するが、最初、上記の Migration コードで正しく新スキーマに変わって安心していたのだが、そのまま開発が進んだあと self.down してみたくなってやってみると、全然動かない。まったく正しそうに見えるのに、おかしいなと散々悩んだあげく、Bookクラスの関連定義の所為だった。

つまり Book モデルの関連が開発が進んだ状態なので Comments モデルと has_one/belongs_to 関連がり、それが邪魔して動かなかったわけだ。しかし本当に self.down する際には、新しいモデルの状態でバージョンダウンが動くはずだ。上記のコードで言うと、self.downの 中で

book.comment = cmt.content

としているところで、book.comment は comments テーブルの参照として定義されているのがいけない。

原因が分ったらハッとひらめいた。Railsのモデルクラスは ActiveRecord::Base さえ継承していれば、完全な ActiveRecord として動くのであるから、Migration の定義に先立って

class Owner < ActiveRecord::Base end class Comments < ActiveRecord::Base end

と書いておくだけで、純粋にスキーマのメタ情報でのみ動くはずだ。
そしてこの方法は、ちゃんとうまくいく。


しかし、ここまでやってみて、はたと気づいたのだが、あれか。
バージョン管理システムからソースコードを先にMigrationをコミットした状態にまで戻してから、db:migrate VERSION=x すれば良いのだった。まだコミットしたくない開発中のコードがローカルにあると少し面倒だが、どう考えてもこれが自然であった…

続く…

投稿者 iwazawa : 22:55 | コメント (0) | トラックバック

2007年11月04日

条件付きテーブル間リレーションシップ

関連付けるテーブルは同じなのだが、条件によって別のオブジェクトとして関連付けるレコードを決定する方法がないか探してみた。

具体的に言うと、現在、次のようなテーブルがあって、

create table books ( id int(11) not null auto_increment, title varchar(255) default null, comment text )

本のタイトル(簡単のために本の情報をタイトルだけとしている)とコメントが格納できます。ここでコメントの種類が増えて、ネタばれコメントを追加したくなったとする。

一番素朴な方法は、booksテーブルにnetabareカラムを直接追加してcommentカラムと同じ様に持てばいいわけだが、そもそもコメントは本の情報ではないし、ネタばれコメントにいたっては更にそうだ。

そこでコメントを別テーブル化したくなる。

class Book < ActiveRecord::Base has_one :comment has_one :netabare : class Comment < ActiveRecord::Base belongs_to :book : class Netabare < ActiveRecord::Base belongs_to :book :

とかするわけであるが、これだとモデルクラスとテーブルが2つ増える。

コメントとネタばれは、ユーザの用途は違うがDBに持つ情報としてはほぼ同じであるので、commentsテーブルは共通にできないだろうか。

has_one, belongs_toのリファレンスを見ると、その為に使えそうな:conditionsというオプションが存在した。

commentsテーブルは共通とし、comment_typeというカラムを用意し、これが1の場合、通常のcomment、2の場合ネタばれコメントとして進めてみる。まずはマイグレーションの作成だ。現在コメントはbooks.commentに格納されているので、データの移行も考えた頑張ったマイグレーションを作成する必要がある。

  • 新テーブルcommentsの追加
  • books.commentからcommentsにcomment_type=1でデータ移行
  • books.commentカラムの削除
  • commentsテーブルにbook_id,comment_typeの複合インデックス
  • 続く…

    投稿者 iwazawa : 20:09 | コメント (0) | トラックバック

    2007年11月03日

    RailsによるアジャイルWebアプリケーション開発第2版

    ちょっと前になるが、RailsによるアジャイルWebアプリケーション開発 第2版を買った。
    Rails本はこの第1版に始まり、いろいろ買ったが、やはり一番参照したのはこの第1版だった。内容が古い分(0.9〜1.0あたりか)、Railsの基本を知るのにとても良かったと思う。

    Rails開発のコツもだいたい理解したし、あとはWebの情報でほぼ大丈夫だろうから第2版はいらないかなと思っていたのだが、本屋で手に取ってみると欲しくなる(笑)内容は第1版とほとんど同じで、最初に実際にWebアプリケーションをアジャイルに開発しながらの様子を実況風に説明し(全体の1/4程度)、その後、各機能の詳しい説明となっている。Railsのバージョンも今、最も使われているであろう1.2が中心なので直接コードのサンプルが役に立つと思われる。

    ただ現在もRailsは進化の途中なので、新しいとは言っても、すでに古くなっている部分もある。そこは書籍の弱点でいかんともしがたいが、初めてRailsをやってみようという方のための参考書としてはこれが一番と断言できる。

    さて私はなんでまた、そんな第2版を買ってしまったのかというと、まず第1版には索引に大きな不満があった。本の内容は非常に素晴らしかったが、索引は全く使えなかったのだ。なにか調べようとしても索引から知りたい事へたどり着けない。それが最も気になっていたので、本屋で第2版を手に取った際も、真っ先にチェックしたのは索引だ。

    今回の索引は良くできている(というかこれが普通かも知れないが…)。第1版で索引に割かれているページ数は13ページに対して、第2版では倍増、とまではいかないも23ページ。しかしフォントが小さくなっていることと、余白が少なくなっていることも鑑みると、おそらく倍以上の情報量となっているはずだ。また第1版では技術本としてはあり得ないほど、リファレンス的な使い方ができなかったのが、第2では本文で説明されているほぼ全てのメソッド名や名前がちゃんと載っている。おそらく第1版以降に数多く出版されたRails本の中でも、最も優れた索引だと思う。

    また、第1版では付録であったマイグレーションの説明が、ちゃんと1章割かれていたり、概要の1節だったActiveSupportの説明にやはり1章割かれていたり、RJSの説明が増えていたり。
    さらに以前(と言っても1年ちょっとしか経ってない)とは流行が変わってしまったRailsの本番環境についての説明も大きく修正されていて使えるものになっている。

    全体のボリュームも100ページ以上、繰り返すが余白が少なくなっていることも考えると内容はかなり増えていると思われるが、驚いたことに本の厚さは薄くなっている。これは紙を薄くして実現したようだ。ページの捲り心地やキーボードの手前に開いた状態で置いて参照する際にも安定しやすく、地味だが良い改良点だと思う。値段は\3,800-から\4,200-へ上がってしまったが、内容を考えれば全く不満はない。

    長々と書いてしまったが、そんなわけで、新しいRailsによるアジャイルWebアプリケーション開発 第2版はとてもお勧めです。

    投稿者 iwazawa : 22:46 | コメント (0) | トラックバック

    2007年10月30日

    Helperをhelperメソッドで定義するのとincludeするのと

    RailsのHelperはController内で

    include HogeHelper

    と書くのと、

    helper :hoge

    と書く方法があります。

    恥ずかしながらこの違いがよく分らなかったのだが、いろいろ実験してみた結果、やっと分った気がしてきた。

    まず前者はRubyの文法のinclude ModuleNameなので、Mixinしているのはすぐ分かる。

    つまりHogeModule内に定義されているメソッドはHogeContorollerに定義されているのと同じ意味だ。

    従って、views/hoge/*.rhtml内でも使えるし、もちろんHogeController内でも自由に使える。

    私は後者について悩んでいたのだが、どうやらView側でだけ使用可能な状態にしてくれるらしい。つまりviews/hoge/hehe.rhtmlでは使用可能だが、HogeController内では未定義な状態。

    はたして存在意義は?もちろんセキュリティーのためだと思われる。Controller内のpublicメソッドはアクションとして実行可能なので、Controllerは結構注意して書かなければいけない。

    お気楽にHelper内に書かれたメソッドがControllerのアクションとして実行されると、まずい状況になりそうなことが容易に想像できる。逆に言えばHelperに書いたユーティリティーメソッドを気軽にViewだけで使用可能にしてくれるhelperメソッドによる定義は便利なのだろう。


    作成中のサービスも、よれよれ動くようになってきたので、そんな事もふまえて危ないことをやっていないか、もう一度見直してみる必要はありそうだ。

    投稿者 iwazawa : 00:20 | コメント (0)

    2007年07月10日

    CentOS4.4 x86_64にRails

    細々とRuby on Railsで作成しているアプリはWindows上でInstant Railsを使って開発している。
    ごく一部の機能だが動き始めたので、常時上げて起きたいと思い、CentOSのサーバ上で実行しようとしてみた。

    まずはruby。

    # ruby -version ruby 1.8.6 (2007-03-13 patchlevel 0) [x86_64-linux]

    最近入れ直したらしい(良く覚えていないが(汗)) これはこれでOKだ。gemも入ってる様なので、そのままrailsをインストール。

    # gem install rails -y

    何の問題もない。mysqlはyumで入れてある(CentOS-Testing.repoでmysql 5.0.42)のでruby MySQLアダプタを入れてみる。

    # gem install mysql checking for mysql_query() in -lmysqlclient... no checking for main() in -lm... yes checking for mysql_query() in -lmysqlclient... no checking for main() in -lz... yes checking for mysql_query() in -lmysqlclient... no checking for main() in -lsocket... no checking for mysql_query() in -lmysqlclient... no checking for main() in -lnsl... yes checking for mysql_query() in -lmysqlclient... no *** 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 :

    いろいろと文句を言われるが、-lmysqlclient で怒られているので、mysqlのライブラリが見つからないのであろう。

    # gem install mysql -- --with-mysql-dir=/usr

    としてみてもだめ。うちのはCentOS 4.4 x86_64だった事を思い出し/usr/lib64の下を見てみると、mysqlフォルダがあり、その下に共有ライブラリが色々と格納されていた。そう言うわけで

    # gem install mysql -- --with-mysql-dir=/usr --with-mysql-lib=/usr/lib64/mysql

    で無事成功。

    投稿者 iwazawa : 07:35 | コメント (0)

    2007年06月25日

    Helperの謎

    URLとして、

    http://hogehoge.com/home/

    という表記が良かったので、

    app/controllers/home_controller.rb app/helpers/home_helper.rb app/views/home/index.rhtml

    という3セットを作って、index.rhtml内で使いたいhelperメソッドをhome_helper.rbに記述したのだが、何故かhelperメソッドが見つからないと言われる。具体的には次のようなプログラム。

    # =-=-= home_controller.rb =-=-=-=-=-= class HomeController < ApplicationController def index end end # =-=-= home_helper.rb =-=-=-=-=-= module HomeHelper def hoge(str) str.upcase end end # =-=-= index.rhtml =-=-=-=-=-= <%= hoge('abc') %>

    そこで試しにhome_controller.rbに明示的に以下のように宣言を書いてみると動いた。

    # =-=-= home_controller.rb =-=-=-=-=-= class HomeController < ApplicationController helper :home # 明示的に def index end end

    しかし、home_helper.rb(HomeHelperクラス)は規約に則っているので宣言は必要ないはずだ。おかしい。

    原因を確かめるべく、再びコントローラに記述したhelper :homeの行を削除して元に戻してみると・・・なぜか今度はエラーとはならずちゃんと動く。う〜む、気持ち悪い。

    投稿者 iwazawa : 01:09 | コメント (2)

    2007年06月24日

    size length

    あらかじめ断っておきますが、オチはありません。

    やっぱり「あらかじめ」は最初に使いたい。よく南武線に乗ってから、「京浜東北線が○○の理由により、現在運転を見合わせております。あらかじめご了承下さい。」などという放送がなされることがあるが、まったくあらかじめっていない。それならば、さっき先に来た反対方向、立川方面行きに乗って溝ノ口経由で行きたかった。

    いきなり脱線してしまったが、Rubyの配列の要素数はsizeメソッドでもlengthメソッドでも良いらしい。ずっと何かの本にたまたま載っていたlengthを使っていたが、最近買ったRails本「Ruby on Rails入門―優しいRailsの育て方」ではsizeを使っていたのでアレっと思っただけです。

    ひょっとして、全てのオブジェクトに実装されている?

    irb(main):001:0> [1,2,3].size => 3 irb(main):002:0> [1,2,3].length => 3 irb(main):003:0> "123".size => 3 irb(main):004:0> "123".length => 3 irb(main):005:0> 123.size => 4 irb(main):006:0> 123.length NoMethodError: undefined method `length' for 123:Fixnum from (irb):6 from :0 irb(main):007:0> 123.5.size NoMethodError: undefined method `size' for 123.5:Float from (irb):7 from :0 irb(main):008:0> 123.5.length NoMethodError: undefined method `length' for 123.5:Float from (irb):8 from :0

    Fixnumはsizeがあってlengthがない、Floatはどちらもないのが面白い。Fixnumのsizeは何を表すのだろうと思い、調べたら実装上のバイト数らしい。

    irb(main):018:0> 1234567890.size => 4 irb(main):019:0> 12345678901.size => 8 irb(main):020:0> 12345678901234567890.size => 8 irb(main):021:0> 123456789012345678901234567890.size => 16

    なかなか面白い。しかし、その理由ならFloatにsizeがあっても良いように思われるのだが。まぁ、配列と文字列以外では必要なさそうなので、深く考えない事にしよう(オチなし)

    投稿者 iwazawa : 21:15 | コメント (0) | トラックバック

    デバッグプリント

    自分のための覚え書き。

    デバッグのためログにいろいろと出力する場合、logger.debugに食わせてオブジェクトの内容を簡単にダンプする方法。

    1. obj.to_s
    2. obj.inspect
    3. obj.pretty_inspect

    1.は数値や文字列など段純なオブジェクト向き。コンテナーなどの場合はオブジェクトIDの文字列表記となる。

    2.はコンテナのような複雑なオブジェクトの内容が分るように文字列に変換してくれる。

    3.はもっと見やすく整形した文字列に変換してくれる。

    投稿者 iwazawa : 14:02 | コメント (0)

    2007年06月23日

    reset_column_information

    DBマイグレーションで既に存在するテーブルにカラムを追加後、そこに他の項目値によってデフォルト値を自動で突っ込みたくなり、少々はまりましたので、覚え書き。

    Hogeというモデルがあり、progressという項目に0 .. 9の範囲で進捗度が格納されているとする。

    progressの9は完了なのだが、9に設定された日を格納するcomplete_dateというdate型の項目を追加したい。既存のレコードにはprogressが9で、complete_dateがNULLの場合、complete_dateに今日の日付を入れる。

    で、マイグレーションでは普通にRubyのコードも書ける様なので、こんな風にしてみた。

    class AlterHoges < ActiveRecord::Migration def self.up add_column :hoges, :complete_date, :date Hoge.update_all "complete_date = now()", "progress = 9 AND complete_date is null" end def self.down remove_column :hoges, :complete_date end end

    しかし、うまくいかない。小一時間んで、RailsによるアジャイルWebアプリケーション開発のマイグレーションの項目を読み直して見たところ、発見。

    モデルの変更があったことをActiveRecordに知らせないといけない。よく考えると当たり前だ。具体的には下記をadd_columnとupdate_allの間に挟むということ。

    Hoge.reset_column_information

    RailsによるアジャイルWebアプリケーション開発はRails0.9ベースに書かれていて、脚注と付録で1.0をサポートしている、結構古めの内容だが、Railsの全体と、重要なイディオムを習得するために最近Railsをやり始めた人もやはり目を通しておくべき本だと思います。

    投稿者 iwazawa : 22:13 | コメント (0)

    2007年06月10日

    binding

    タイトルはbindingですが、直接的に組み込み関数のbindingやBindingクラスの事ではありません。関係はありますが。

    最近、書いているAmazon ECSを検索して、ごにょごにょする処理で、ECSのResponseの中(ResponseGroup=Mediumには含まれている)にBindingという項目があります。本の種類(親書、単行本、文庫)などが取得できます。

    そこで、ECSの情報をキャッシュするテーブルEcsBookというクラスを作って、binding項目もそのままbindingという項目名としました。

    class CreateEcsBooks < ActiveRecord::Migration def self.up create_table :ecs_books do |t| t.column :asin, :string ... t.column :binding, :string ...

    そうすると、DBからこのテーブルのレコードを読んでくる際

    TypeError (wrong argument type String (expected Proc/Binding)): Y:/usr/local/InstantRails/ruby/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/callbacks.rb:335:in `eval' ...

    こんなエラーが発生するようになった。

    実は、このエラーがこの項目bindingのせいだと気づいたのは、何時間も悩んだあげく、Ruby on Rails: beware of reflection errors [<blink> Tech notes for the greater good]というページをググって見つけたから。

    英語は不得意なもので、正確には分らないですが、evalやbindingとかBugとか言っている。
    これはActiveRecordがbindingというカラム名を誤認識して、リフレクションがおかしくなるのでは?と気づきました。

    項目名bindingをecs_bindingに変えてみたところ見事、問題解決。

    しかし分りにくいな。

    投稿者 iwazawa : 00:26 | コメント (0) | トラックバック

    2007年06月09日

    ISBNでAmazon ECS検索(2)

    先日のAmazon ECSを検索するサンプルクラスですが、いろいろと問題が。

    まず、致命的なところでは、このクラスItemSearchとItemLookupに対応しているつもりなのに、ItemLookup時のエラー応答がハンドリングできていない。

    xpath = "ItemSearchResponse/Items/Request/Errors/Error"

    というXPathでエラー応答があるかどうかをチェックして、エラーの場合はコードとメッセージを取得しているのだが、ItemSearchをハードコード(死)

    xpath = "#{@response_node_name}/Items/Request/Errors/Error"

    こうだった。

    また、このクラスはlibディレクトリの下において、Controller/Helper/Modelとは別扱いにしたのだが、そうするとloggerオブジェクトも使えなくて困った。上記のエラーコードとメッセージをlogger.warn()しておきたかった訳です。

    しかしよく考えると、Controller/Helper/Moddelからloggerオブジェクトをいつでも使えると言うことは、loggerはクラスメソッド扱いか。明示的にクラス名を指定して

    @error_code = element.text('Code') @error_message = element.text('Message') ApplicationController.logger.warn 'error_code: ' + @error_code ApplicationController.logger.warn 'error_message: ' + @error_message

    でOKだった。

    追記:

    ApplicationController.logger を明示的に指定すれば確かに良いのだが、長い。なんで、

    @logger = ApplicationController.logger def logger @logger end

    とかやっておけば良いのでしょうか。Rubyっぽくない気もするが、よく分かりません。

    投稿者 iwazawa : 22:36 | コメント (0) | トラックバック

    2007年06月06日

    プログラミングRuby第2版

    Railsのコードを細々と書いています。

    やりたいことをRubyの文法でどう表現するのかとまどうことが多く、思い切って週末にプログラミングRuby 第2版 言語編を買ってきました。

    早速続きのコードを書きながら、分らない事を索引で引いて、ページを開く事を数回続けた結果、まったく索引のページにその記述がない!こんなに間違いの多い索引を平気で出版するとはどういうことかと、出版社に文句のメールを書こうかと思い始めたところ、索引の最初のページの脚注に説明がありました。

    この日本語版では、元々1冊だった英語版原書を『プログラミングRuby第2版言語編』と『プログラミングRuby第2版ライブラリ編』に分冊しているため、次のように区別しています。ゴシック体はこの巻のページ番号です。斜体は他方の巻のページ番号です。

    皆様もお気を付け下さい(汗)

    プログラミングRuby 第2版 ライブラリ編も欲しいが、両方合わせて¥8,200円。うむ。

    投稿者 iwazawa : 00:58 | コメント (0) | トラックバック

    2007年06月04日

    ISBNでAmazon ECS検索

    慣れない言語に四苦八苦しながら(といいつつ、かなり楽しんでいる)、Amazon ECSからデータを引っ張ってくるクラスを書いてみた。
    まだRubyに不慣れなので、かなりアレなコードだと思われるが、夢中になってるとブログに書くのも忘れるので、とりあえずのネタとして。

    require 'net/http' require 'cgi' require 'rexml/document' class AmazonEcs attr_reader :error_code, :error_message @@common_params = { :Service => 'AWSECommerceService', :AWSAccessKeyId => 'XXXXXXXXXXXXXXXXXXXX', :Version => '2007-05-14', :AssociateTag => 'xxxxxxxxxxx', :Operation => 'ItemSearch', :ResponseGroup => 'Medium,BrowseNodes' }; def initialize(book) @sw = SearchWord.new @sw.set_by_book(book) end def item_search h = {} @@common_params.each do |key,val| h[key] = val end if @sw.isbn.blank? h[:SearchIndex] = 'Books' h[:Title] = @sw.title unless @sw.title.blank? h[:Author] = @sw.author unless @sw.author.blank? h[:Keyword] = @sw.keyword unless @sw.keyword.blank? @response_node_name = 'ItemSearchResponse' else h[:SearchIndex] = 'Books' if @sw.isbn.length > 10 h[:IdType] = 'ISBN' if @sw.isbn.length > 10 h[:Operation] = 'ItemLookup' h[:ItemId] = @sw.isbn @response_node_name = 'ItemLookupResponse' end url = mk_url(h) @xml = aws_request(url) if has_xml_error?() return nil end first_ecs_book() end def mk_url(h) # RESTで必要なパラメータをURL形式に変更 arr = [] h.each do |key,val| arr << key.to_s + "=" + CGI::escape(val.to_s) end paramString = arr.join('&') URI.parse("http://webservices.amazon.co.jp/onca/xml?" + paramString) end def aws_request(url) return REXML::Document.new(Net::HTTP.get(url)) end def has_xml_error? # エラーチェック xpath = "ItemSearchResponse/Items/Request/Errors/Error" @xml.get_elements(xpath).each do |element| @error_code = element.text('Code') @error_message = element.text('Message') return true; end return false; end def first_ecs_book xpath = "#{@response_node_name}/Items/Item" @xml.get_elements(xpath).each do |element| ecs_book = EcsBook.new ecs_book.asin = element.text('ASIN') ecs_book.isbn = element.text('ItemAttributes/ISBN') ecs_book.title = element.text('ItemAttributes/Title') ecs_book.author = element.text('ItemAttributes/Author') ecs_book.manufacturer = element.text('ItemAttributes/Manufacturer') ecs_book.binding = element.text('ItemAttributes/Binding') ecs_book.publication_date = element.text('ItemAttributes/PublicationDate') ecs_book.small_image_url = element.text('SmallImage/URL') ecs_book.medium_image_url = element.text('MediumImage/URL') ecs_book.large_image_url = element.text('LargeImage/URL') ecs_book.detail_page_url = element.text('DetailPageURL') return ecs_book end nil end end

    中で使われている、SearchWordとEcsBookというクラスはそれぞれ、画面からの検索情報を渡すためのプレースホルダー、およびAmazonECSからの結果を保持し、DBへ格納するためのActiveRecordです。

    ISBNが指定されている場合は、優先してISBN検索をするようにしたのだが、最近買った本(具体的には泣き虫弱虫諸葛孔明第弐部)のISBNを入れてみたところ、うまくいかない。

    この本はISBNが従来の10桁ではなく13桁になっている(詳しくはWikipediaを参照のこと)。

    ISBNが11桁以上だったら、IdTypeパラメータをISBNに設定(このとき、SearchIndexパラメータも必要となる)すると、ItemLookupオペレーションが動くことを発見。ちなみに10桁のISBNは、ASINと一致なので、IdTypeはASIN、もしくは指定しない(=ASIN)である。ASINの場合、面倒だがSearchIndexパラメータが指定されると逆にエラーとなる。
    APIドキュメントによると、JPではIdType=ISBNはサポートされてないことになっているが、使えるから良いか。

    どうせなら10桁の時も、IdType=ISBNで取得させてくれれば楽だと思うのだが、そうはなっていない。

    投稿者 iwazawa : 02:33 | コメント (0) | トラックバック

    2007年06月02日

    blank?

    RailsのおさらいもかねてWEB+DB VOL.32からの連載「Ruby on Rails Way」を読み返してみた。

    そこでblank?メソッドを発見。前にも読んでるはずなのだが、やはり必要に迫られていないと、全く覚えていない物だ。

    if str.nil? || str.empty? ... end

    を一発で

    if str.blank? ... end

    と書けて気持ちがよい。Javaで仕事していたときは、Rubyの様な特異メソッドなぞは使えないので、普通に

    if (Utils.isBlank(obj.getProperty())) { ... }

    なんて、やってたのを思い出す(笑)

    投稿者 iwazawa : 23:59 | コメント (0) | トラックバック

    2007年05月29日

    RadRailsでステップ実行

    EclipseのRuby on Rails開発環境、RadRails and Ruby Development Tools (RDT)がバージョンアップ(といっても5/14の様だが)されていて、ついにEclipse内でステップ実行が出来るようになっている。

    コードを追いかけながら動きを掴めるのは初学社にはありがたい。

    ServersビューのWEBrickやMongrelサーバのプロパティーを表示すると、「Run Mode」というのが追加されていて、ここを「run」から「debug」に変更するとOK。

    radrails_runmode.jpg

    既にインストールされている場合は「Help」メニューの「Software Updates → Manage Configuration → Search for updates of the currentry installed features」からRadRailsが最新かどうかの確認と、最新ではない場合はアップデートできる。

    まだ入ってないなら「Software Updates → Find and Install... → Search for new features to install」で http://update.aptana.com/install/rails/3.2/ をURLに追加して続ければOK。


    ただし、かなり動作が遅くなるので、常時debugモードにしておくとストレスがたまりそうですが(笑)

    投稿者 iwazawa : 07:29 | コメント (0)

    2007年05月25日

    Amazon ECS

    Amazon ECSですが、以前のエントリーamazon-ecsを使うつもりと書いたのですが、これカテゴリーのトラバースをするOperationであるところのBrowseNodeLookupに対応していなさそうな感じ。

    カテゴリーは結構使えそうな情報なので、他を当たってみたところ、素直(なのかどうなのか分りませんが)にNet::HTTPREXMLで自分でやっても大したこと無さそうだという結論に。

    試しにBrowseNodeLookupでサブカテゴリーにどんどん降りていくRailsアプリを書いてみたが、わりとすぐにできた。それもリクエストとレスポンスは開始1時間後にはできていて、そのあとREXMLの使い方がイマイチ分らず悩んでいただけでした。

    やってておかしくないのにもかかわらず、XMLは何故だか使ったことがなかった私(笑)

    投稿者 iwazawa : 00:25 | コメント (0) | トラックバック

    2007年05月22日

    text_field と text_field_tag

    Ruby on RailsでViewに<input type="text"/>タグを出力する際、

    text_field() と text_field_tag() があって、どちらを使えばよいのか分らず暫くキョトーンとしていたのですが、やはりライド・オン・Railsは優れています。最後の「付録 Appendix」のリファレンスに一言で分りやすく記載されていました。

    ヘルパメソッドリファレンス(P353, 357)より参照。

    ■text_field() 達のグループ。
     ActionView::Helpers::FormHelper
     モデルを対象とするヘルパです。

    ■text_field_tag() 達のグループ。
     ActionView::Helpers::FormTagHelper
     モデルオブジェクトではなく、値を対象とするヘルパです。

    すなわち、

    ・前者はテンプレートにアサインされたRailsのモデルオブジェクトとその属性にアクセスできるように、
    ・後者はHTMLタグの名前と値からアクセスできるように

    HTMLタグをレンダリングするということらしい。前者が高レベル、後者が低レベルなAPIとも言えましょうか。

    ちなみに、同じくここで知りましたが、tag()、content_tag() というパラメータで渡された名前とハッシュによる属性指定(および、content_tag()の場合はcontent部)によって、どんなタグでも生成できる、ヘルパーも発見。

    divの様なタグを<%= %>の中で生成できて、Rubyの変数へのアクセスがし易くなり、コードも見やすくなる効果があるそうだ。

    良く考えられている。

    例によって、これで1時間近く悩んでいたのであった。そんな訳でコーディングがなかなか進まない。しかし納得したので気持ちは良い。

    投稿者 iwazawa : 00:01 | コメント (0)

    2007年05月19日

    InstantRailsとRuby-GetText-Package

    当然作るアプリは最初、日本語が使えれば良いということで、viewなどはガリガリ日本語で直接書いてしまうのが、手っ取り早くて良いような気がした。

    しかし性格的に全体がカッチリしてないと次に進めないのか、多言語対応するための仕組みを調べ始めてしまう。めっちゃ参考にしているライド・オン・Railsでは、Special-GenerationとRuby-GetText-Packageが紹介されている。

    Supecial-Generationはscaffoldジェネレータの拡張で、多言語対応というより、Ruby on Railsの中の日本語に対応しているミニフレームワークと言った雰囲気。ちょっと私の趣味に合わない気がしたのでスルーの方向で。

    Ruby-GetText-Packageはスタンダードっぽい。こっちを採用し、ごにょごにょ始めることに。

    躓いたのは、InstantRailsでpoファイルのマージが出来ないというところ。ライド・オン・RailsではRakefileを拡張し、rakeタスクとしてpoファイルを作成できるようにしているが、内部でmsgmergeというコマンドが使われており、それが見つからないらしい。msgmergeはGnu GetTextのコマンドの様だ。

    > rake update_po (in Y:/usr/home/iwazawa/workspace/hogehoge) po/hogehoge.pot Y:/usr/local/InstantRails/ruby/bin/rake.bat: No such file or directory - msgmerge po/hogehoge.pot tmp.pot rake aborted! You have a nil object when you didn't expect it! You might have expected an instance of Array. The error occurred while evaluating nil.empty? Y:/usr/home/iwazawa/workspace/hogehoge/rakefile:16 (See full trace by running task with --trace)

    InstantRailsのRubyのインストールセットの中にrmsgmergeというコマンドがあり、これをGnu GetTextの代わりに使えるという情報をゲットした。やり方は<InstantRails install path>\use_ruby.cmdファイルに

    set MSGMERGE_PATH=<InstantRails install path>\ruby\bin\rmsgmerge.cmd

    という行を追加するというもの。これではInstantRailsのruby用コマンドプロンプトからしか実行できないが、radrailsでPATHを指定する方法が分らず妥協するとする。

    あと、use_ruby.cmdファイルの冒頭のコメント部に、「このファイルはInstantRailsによって生成されるので、テンプレートな<InstantRails install path>\conf_files\use_ruby.cmdファイルを編集するように」といった、お達しがあるのだが、言われるままに変更してもいっこうに、コマンドプロンプトの環境が変わらなかったため、仕方なく直接書き換えました。

    投稿者 iwazawa : 17:57 | コメント (0)

    2007年05月14日

    意外と進まない

    Railsでコードを書き始めたのだが、意外とサクサク進まない。

    こちとら、JavaでHibernateでバリバリ書いていたという自負(苦笑)があり、Railsのテーブル関連も、最初にRailsによるアジャイルWebアプリケーション開発を読んだとき、特に引っかかることもなくすらすら読めたし、Railsのmigrationも良くできている。Controllerだって、Strutsに比べたら簡単そうだ。Viewに関しては更に、である。

    しかし実際にコードを書きながら進めていくと、この場合、モデルやコントローラを作るのか作らないのか、名前はどうするの?など、規約を知らないために右往左往してしまっている。本を読んでいると、分らないことは特になく、何でも今すぐ作れそうな気がしてしまうが、自分がやろうとしていることを、どうやるのかがすぐには分らない。

    ウリであるところの規約は、規約自体を知らなければ意味がないと言うことです(笑)

    いや別にRailsをけなしているわけではなく、これは当たり前のことで、今までの開発ツールであれば、

    (1) 設定方法を調べ
    (2) 設定し
    (3) 設定を使う

    といった3段階でやっていたことを、

    (1) 規約を調べ
    (3) 規約を使う

    の2段階にしてくれる上に、感覚的には前者の(1)、(2)の難易度は後者の(1)よりも相応難しかったはずです。

    ツールの使い方につても、習熟が足りない。generate modelやscaffoldや、rake db:migrateやその他、いろいろをいつやって、何ができて、本当にやっていいの?など迷う。

    今はまだRailsをさわり始めたばかり(しかもほぼ休日の数時間)なため、なかなかノリが見えてこないが、慣れたらいけるはず。出来る子のはず(笑)

    ある程度の規模のアプリを書くためにはRailsの規約の何パーセントくらいが分ったらサクサクいけるのかを身をもって体験していきたいと思います。今のところ…20%くらいか(自己申告)

    投稿者 iwazawa : 00:04 | コメント (0)

    2007年05月13日

    Amazon ECS

    連休にRuby on Railsでいろいろと試していたのは、もちろん作りたいサービスがあるからです。
    それにはAmazon ECSを使うつもりであります。

    処理自体は先日試した amazon-ecs プラグインで比較的簡単に書けそうですが、ECSからどのような情報が取得できるのかなど、具体的なところは、ちゃんとAmazon E-Commerce Service Developer Guideを見ないと分りません。

    そして、もちろん英語なのでどの情報が必要なのか、いささか苦労しながら(笑)探したtころ、当面必要である、書籍の検索には次のリファレンスを見れば分りそうです。

    上から、


    • 操作の種類の一覧
    • 商品検索操作の詳細
    • 結果取得グループの一覧
    • Mediumグループの結果の項目

    といったところでしょう。Response Group(結果取得グループ)には用途によって様々な結果の取得パターンがあるようです。おそら私はくMedium Groupがちょうど良さそうです。

    明日は早速、検索部分を作ってみたい。

    投稿者 iwazawa : 01:37 | コメント (0) | トラックバック

    2007年05月06日

    amazon-ecs

    ライド・オン・Rails ですが、面白そうなので、リファレンス系のページはばっさり飛ばして、「Amazon Webサービスとの連携」やってみます。

    本ではruby-amazonを使っての説明ですが、ちょっと調べると、amazon-ecs がよさげ。gemで簡単にインストールできます。

    gem install amazon-ecs

    そうするとサンプルも書き直さないといけない。こんな感じか。

    def amazon_search @books = [] if request.post? require 'rubygems' require 'amazon/ecs' access_key = "your access key" associate_id = "your associate id" country = :jp Amazon::Ecs.options = { :aWS_access_key_id => [access_key], :associate_tag => [associate_id], :country => country } res = Amazon::Ecs.item_search(params[:keyword], { :search_index => 'Books', :sort => 'salesrank', :response_group => 'Medium' } ) if res.has_error? flash[:notice] = "Amazon ECSよりエラー:" + res.error return end res.items.each do |item| book = Book.new book.isbn = item.get('isbn') book.name = item.get('title') book.author = item.get('author') smallimage = item.get_hash('smallimage') if smallimage book.small_img_url = smallimage[:url] else book.small_img_url = 'noimage.gif' end book.manufacturer = item.get('publisher') book.release_on = item.get('publicationdate') @books.push book end end

    本ではruby-amazonが返すオブジェクトをそのまま使ってビューをレンダリングしてるが、amazon-ecsはイメージURLの持ち方が単純でないので、Bookモデルに詰め替えることにします。small_img_urlというフィールドを勝手に追加してます。
    この辺、Rubyに慣れてないんでアドホックですが、たぶんそのうちクールな方法を習得するはず。今は一杯一杯。

    Ruby Amazon E-Commerce REST Service API (amazon-ecs)によると、

    # or you can also do it this way atts = item.get('itemattributes') atts.get('title') # get only returns the first element of an array atts.get('author') # 'Author 1'

    という風に、AmazonのAPIドキュメントAttributes of Items in the Book Storeに忠実に取得できて気持ちいいと思い、使おうとしたのですが、

    atts = item.get('itemattributes')

    が返すオブジェクトがgetメソッドがないと仰る…それで色々と試した結果、itemオブジェクトから直接getはできる様なのでそっちでやりました。

    そして、これを表示するビューは、こんな感じか、

    <%= form_tag %> keyword: <%= text_field_tag 'keyword', params[:keyword] %> <%= submit_tag 'search' %> <%= end_form_tag %> <table> <tr> <th>ISBN</th> <th>authors</th> <th>image (small)</th> <th>manufacturer</th> <th>product_name</th> <th>release_date</th> </tr> <% @books.each do |book| -%> <tr> <td><%=h book.isbn %></td> <td><%=h book.author %></td> <td><%=image_tag book.small_img_url %></td> <td><%=h book.manufacturer %></td> <td><%=h book.name %></td> <td><%=h book.release_on %></td> </tr> <% end -%> </table>

    まだまだ拙いが、なんとなく分ってきたかな。

    早くRuby on Railsを自由自在に使いこなしてアプリを作り始めたいのだが、そうこうしているうちに大連休の最終日夕方(死)
    やはり、そう甘くはないと言うことで(笑)しかしRuby on Railsはこの先、相当な武器になりそうなので、やっていく。

    投稿者 iwazawa : 18:31 | コメント (0) | トラックバック

    DynamicCalendarHelper

    まだまだライド・オン・Rails 頑張ってます。この本、もう少し先の「Railsエッセンシャルズ」という章を先読みしたのですが、リファレンスとしても、非常に優れてました。解説も非常に分りやすいし、記載の内容が少し古い(書籍では仕方ないですね)以外は、とても満足してます。買って良かった。

    グラフィカルなカレンダーを表示するという所で、CalendarHelperというのが紹介されていて、実際にサンプルを動かすのですが、記載のサイトに行ってみると、DynamicCalendarHelperと、名前もグレードアップしている雰囲気です。

    本では.rbファイルをダウンロードしてきて所定のフォルダにコピーというインストール方法で説明されていますが、プラグインのインストールに対応したようです。

    ruby script/plugin install http://topfunky.net/svn/plugins/calendar_helper/

    で、サクっとインストール完了。

    しかし、ということは、本の通りにはいかないということで、やはり最初の例でつまずく。
    まずは


    vendor/plugins/calendar_helper/README

    を読んでみると、
    script/generate calendar_styles

    をしとくようだ。あとはRDocを読めと書いてある…Rails、というかRuby初心者には、すぐにはどうして良いのかわからない。

    今までの経験を元に、application.rbにrequire 'calendar_helper'を、application_helper.rbにinclude CalendarHelperを追加したところ、なんと成功!

    投稿者 iwazawa : 02:10 | コメント (0) | トラックバック

    2007年05月05日

    LoginEngine

    引き続きライド・オン・Rails を進めております。

    サンプルでlogin_engineというユーザ管理を全部やってくれる仕組み(プラグイン?)を使っているので、言うとおりにやっていたのだが、またもハマる。db:migrateができない。本の内容にくわえて、ちゃんとバージョンが違った場合に備えてLoginEngineのREADMEも参照しながらやったのだが、全くうまくいかない。

    どうも、LoginEngine.configというメソッドを呼べないらしいのだが、environment.rbに言うとおりの記述を追加しているし、なんだか悪くないと思われるのだが。

    またも行き詰まってしまったので、Google先生に頼ってみると。見つけた。

    Rails 1.2.3でEngines及びLogin Engineプラグインを利用する

    このレポートは激しく素晴らしい。これがなかったら私もEngines 1.2 Releasedを、さらに拙い英語力で読み解いている間に休日が終わってしまうところでした。感謝感激であります。

    しかし、Rails思ってた以上に躓いてしまう。まぁ、あんまり簡単だと、それはそれでつまらないから良いか(笑)

    投稿者 iwazawa : 17:19 | コメント (4)

    application.rb

    ライド・オン・Rails 62P より

    すべてのコントローラ共通の振る舞いはapp/controllers/application.rbに記述します。これは、すべてのコントローラクラスはApplicationControllerを継承するからです。

    これは、本質的に重要っぽい。良く覚えておこう。

    フレームワーク全体のノリが分るようになるまで、なかなか大変。面白いけど。

    login_engineを使ってみる説明の所なのだが、ApplicationControllerのbefore_filterにフィルタを噛ましている。これでこのアプリケーションの全アクションでlogin_requiredを強制させられるという寸法か。

    投稿者 iwazawa : 15:50 | コメント (0) | トラックバック

    content_for_layout と yield

    ライド・オン・Railsのlayoutファイルを修正する所で、

    … <%= @content_for_layout %> <br /> …
    と @content_for_layout というインスタンス変数が出てくる箇所が、scaffoldが生成したコードでは yield という変数(?)に変わっていた。

    ググったところ、SaikyoLine.jpの@content_for_layoutって。というのを発見。なるほどです。しかし、これ去年の9月のだ。私はかなり遅れているなぁ。

    投稿者 iwazawa : 14:38 | コメント (0) | トラックバック

    ヘルパーモジュール

    ライド・オン・Rails 56P より

    「ビューで共通に使うメソッドは、ヘルパーモジュールの中に定義します」

    こういう情報は、サラっと書かれているが重要だ。おそらくRailsに慣れ親しんでいる人達にとっては、当たり前。当たり前すぎて書かれないことが多い類の情報と見ました。井沢元彦の受け売りですが。

    投稿者 iwazawa : 14:16 | コメント (0)

    before_filter

    これは、なかなか素敵な仕組みだ。認証チェックにはやっぱり普通、コレですかね?
    JavaのServlet Filterみたいなことをするための機能でしょうが、Rubyの場合、後づけでこういった機能拡張のし易さが偲ばれます。

    投稿者 iwazawa : 13:24 | コメント (0) | トラックバック

    ライド・オン・Rails

    ライド・オン・Rails通りに進めているわけですが、formの機能の単体テストを実行すると

    DEPRECATION WARNING: end_form_tag is deprecated and will be removed from Rails 2.0 See http://www.rubyonrails.org/deprecation for details. …

    とか言われてしまう。廃止予定っていいな。end_form_tagって識別子がよっぽど気に入らなかっただろうか(笑)

    end_form_tag は単なる end に変わるらしい。Rails Forumを参照。

    しかし、格好いいかどうかは別にして、私は end のみより end_form_tag の方が読みやすい気がするのだが。

    ムム。上記の DEPRECATION WARNING ですが、すぐに対応したらダメなようだ。動かなくなってしまった。

    Rails 2.0が出るまでは end_form_tag を使えということか。とすると、このWARNINGが気持ち悪いなぁ。

    投稿者 iwazawa : 12:45 | コメント (0)

    test:unitsの続き(2)

    また、前回の続き。

    できた!
    プロジェクトの設定を見ていても全くうまくいきそうな気がしなかったので、Instant RailsのMySQLの起動設定ファイル、my.ini (Windows以外のプラットフォームだとmy.confのことですね?)で

    default-character-set=utf8

    としたら、ちゃんとutf8でDBとテーブルが生成されるようになり、文字化け解消。単体テストtest:unitsも見事に通りました。

    ちなみにInstant Railsのmy.iniファイルはExplorerから探しまくらなくとも、メニューのConfigure⇒MySQLで開けます。

    ただ、これって、そういうもんなのか。何となく腑に落ちない。database.ymlの指定くらいはちゃんと見てくれても良いと思うのだが。

    確かにライド・オン・Rails Ruby on Railsを徹底攻略ではMySQLのインストールでデフォルトの文字セットは「Best Support For Multilingualism」を選べとなっていて、そうするとutf8になるのだろう。

    これはInstant Railsが日本で使うにはちょっとインスタントじゃなかったという事だ。しかしもっと早く気づいても良いとは思う>自分。

    投稿者 iwazawa : 01:36 | コメント (0)

    test:unitsの続き

    前回の続き。

    db:test:clone_structure というrakeタスクがあるのを見つけたので、実行してみると、ちゃんとutf8でDBとテーブルができた。

    しかし、test:unitsを実行すると、またlatin1_swedish_ciである。君はスウェーデンが好きだな!

    投稿者 iwazawa : 01:06 | コメント (0) | トラックバック

    Rails

    Railsやってます。RailsによるアジャイルWebアプリケーション開発は随分前から持っていて、通勤時間などを使って、わりと読んだんですが、やはり知識と実地はかなり違っていて、じっさいにやってみると手が止まる。ちょっと、この本、通しでやるには重くて、この休み中それだけで終わってしまいそうな雰囲気。
    本屋に月刊マガジンを買いに行くついで(?)に他のRails本を見てみると、ライド・オン・Rails Ruby on Railsを徹底攻略が目にとまった。短時間に一通り会得するのに良さそうだ。時間節約のために3000円使った(笑)

    しかし、今までの経験から、どうせ色々細かいところはWEBの権威あるソースを参照しないといけないとは思います。

    そして、さっそく夕飯後に初めたのだが、やはりつまらないところでハマる。単体テスト実行するってところで、言うとおりにすると、テーブルのデフォルトエンコードが latin1_swedish_ci なるエンコードになり、データはutf8で入れてるので文字化け。DB自体をutf8で作っておいても、latin1_swedish_ciで作成し直される始末。開発用DBのmigrateはうまくいくのだが、test:unitsだとそうなる。database.ymlでも全てのスキーマ定義でちゃんとutf8を指定しているし、念のために ActiveRecord::Migration の create_table にデフォルトをutf8にするオプションを書いても、test:unitsはダメ。あう〜。

    投稿者 iwazawa : 00:14 | コメント (0)

    2007年05月03日

    Instant Rails

    数ヶ月前にRuby on Railsを試してみようと調べたところ、Instant Railsが楽だったので、今回も使うことにして、バージョンをチェックすると1.3a。
    今の最新は1.7という事なんで、ダウンロードだ。

    How To Upgradeによると、


    • 既存のディレクトリに上書きするな!新しいディレクトリに展開すべし。
    • DBデータがあるなら、.../mysql/dataをコピーすべし。MySQL 4.x→5 はdumpしてimoprtすべき。
    • 各種設定ファイルを変えてたら、コピーすべし。それはApacheのhttpd.conf、MySQLのmy.ini、PHPのphp.iniの事。
    • railsアプリケーションをコピーすべし。
    • Instant Railsを起動すべし。新しいパスで設定ファイルがアップグレードされる。
    • environment.rbを編集すべし。RAILS_GEM_VERSION = '1.2.3'に変更しないとmigrateが起動しない。

    前のInstant Rails 1.3aからは特にこれといって引き継ぐこともないので、全部やらなくても大丈夫そうだ。

    アップグレードしなきゃいけない人で気をつけるべき所は、おそらくMySQLのデータ移行だろう。ChangeLogを見ると、バージョン1.6でMySQLのバージョンが5.0.27に変わっている。前は4.1.x系だったので説明の通りdump/importが必ず必要でしょう。まぁInstant Railsはそれぞれのフォルダ単体で起動するので、既存ディレクトリに上書きさえしなければ、古いのを起動してdump、新しいのを起動してimportとやれば簡単なはず。


    そして、予想通りサックリ起動。phpMyAdminにもサックリ接続。サンプルアプリケーションもサックリ起動し、サックリ接続。さすがInstant Rails。まず最初の作業はMySQLのrootユーザにパスワードを設定して、開発用DBユーザを作成する事からか。

    サンプルアプリのtypoですが、簡単なBlog管理ツールだ。えらい良くできてるなぁと関心しきり。ただ記事のポストができなかった。なにか設定が足りないのかも知れない。このコードは後で参考になりそう。

    投稿者 iwazawa : 17:20 | コメント (0)