#chiroito ’s blog

Java を中心とした趣味の技術について

JRuby on Railsシステム構築入門 5章 -GAE版-

 『JRuby on Railsシステム構築入門』の内容をGoogle App Engineで動作させてみたいと思います。本内容をblogに記載することの許可と協力をして頂きました著者である橋本 吉治 氏(@hasssie)に感謝致します。なお、具体的な説明は行いません。詳細は書籍を参照願います。
JRuby on Railsシステム構築入門 (DB Magazine SELECTION)
JRuby on Railsシステム構築入門 (DB Magazine SELECTION)

橋本 吉治
4章はこちらJRuby on Railsシステム構築入門 4章 -GAE版-

サンプルソース

 記事中で変更点の全てを説明することが困難になってきたためrailsのapp以下だけですがサンプルソースを用意しました。サンプルソース
 そのため、記事中では変更点のキーポイントだけを解説いたします。詳細はソースをdiffして下さい。

例外

ActiveRecordの例外をDataMapperへ置き換えます。

ActiveRecord::RecordNotFound -> DataMapper::ObjectNotFoundError
ActiveRecord::RecordNotSaved -> DataMapper::SaveFailureError

検索条件(books_controller.rb)

 app/controllers/books_controller.rbの検索処理を修正します。DataMapperでは全検索はモデル.allで行います。今回はborrowedがfalseのものを検索対象とします。
# 書籍
@books = Book.find :all, :conditions => {:borrowed => 0}
# GAE
@books = Book.all(:borrowed => false)

ソート順(users_controller.rb)

app/controllers/users_controller.rbでは検索結果のソートを行います。
# 書籍
@users = User.find(:all, :order => :name)
# GAE
@users = User.all(:order => [:name.desc])

制約(user.rb)

 モデルクラスへ制約を加えてみます。対象はユーザモデル(app/models/user.rb)です。
今回は
  • ユーザ名に必須・一意制約
  • 入力されるパスワードには6文字以上15文字以下
  • 入力確認フィールド
を追加をします。
# 書籍
validates_presence_of :name
validates_uniqueness_of :name
validates_length_of :text_password, :within => 6..15,
:too_short => "Password must be more than 6 characters.",
:too_long => "Password must be less than 15 characters."
attr_accessor :password_confirmation
# GAE版
property :name, String, :required => true, :unique=>true
validates_confirmation_of :text_password, :confirm =>:password_confirmation
validates_length_of :text_password, :max => 15,:message => "Password must be less than 15 characters."
validates_length_of :text_password, :min => 6,:message => "Password must be more than 6 characters."
attr_accessor :password_confirmation
参考:DataMapper - Validations

多対多関連

 GAEではbookとbookshelfの多対多の関連にpossessionの様なテーブルは必要ありません。詳しくはこちらにまとめました「chirokings: GAE+JRuby+Datamapperで多対多
  def bookshelves
if self.bookshelf_ids == nil
[]
else
Bookshelf.all(:id=>self.bookshelf_ids)
end
end
def add_bookshelf(bookshelf)
self.bookshelf_ids = self.bookshelf_ids.to_a << bookshelf.id
end
def remove_bookshelf(bookshelf)
self.bookshelf_ids = self.bookshelf_ids.to_a
self.bookshelf_ids.delete(bookshelf.id)
end
private
property :bookshelf_ids, List
参考:DataMapper - Associations

トランザクション

 GAEでは異なるエンティティグループに属するエンティティは一つのトランザクションで実行する事が出来ません。今回のケースではbookとbookshelfのエンティティを1つのトランザクションで保存するには同一のエンティティグループに属させなければいけません。現状では同一のエンティティグループに属していないためトランザクションを行うと下記のエラーが発生します。
ArgumentError (java.lang.IllegalArgumentException: can't operate on multiple entity groups in a single transaction.):
 Datamapperではこのあたりのことは現在進行中というとで解決策は今のところありません。おそらくこのデータモデルでは解決することは困難と考えられます。それはRDBでのデータモデルのままではGAEへの単純移行は困難になる可能性を秘めています。そのため、サンプルソースではトランザクションを行わないようにしています。
Support for some datastore features like transactions and entity groups are in progress.
参考:トランザクション - Google App Engine - Google Code
参考:appengine-jruby - Google App Engine API Wrappers and Tools for JRuby - Google Project Hosting