#265 Rails 3.1 Overview
Rails 3.1の最初のベータ版がリリースされました。今回から数回にわたって新機能を紹介していきます。今回のエピソードでは、環境のセットアップ方法について説明し、その後で新機能の概要を見ていきます。
Rails 3.1をインストールする
Rails 3.1 beta gemをインストールする前に、rvmを使用してgemsetを作成し、インストールする環境をその他の環境から隔離してRails 3.0のインストール環境に影響を与えないようにします。次のコマンドを実行して、railspre
というgemsetを作成します。
$ rvm 1.9.2@railspre --create
次に以下のコマンドを実行して、このgemsetの下にRailsベータをインストールします。
$ gem install rails --pre
これによっていくつかのgemがインストールされます。すべてが正しくインストールされたことを確認するためにRailsのバージョンをチェックします。
$ rails -v Rails 3.1.0.beta1
これでベータ版がインストールされたので、最初のRails 3.1アプリケーションを作成できます。
$ rails new todo
Rails 3.1はデフォルトのJavaScriptライブラリとしてjQueryを使用します。 Prototypeを使用して新しいアプリケーションを作成したい場合は、-j
オプションを指定します。
$ rails new todo -j prototype
新しいアプリケーションを見ていく前に、そのアプリケーションのディレクトリにcd
し、bundle
を実行してgemをインストールします。インストールが完了したら、アプリケーションの構造を見てみましょう。
TextMateで見るとディレクトリ構造はこのようになっています。(ここでは、デフォルトのドロワ(drawer)ではなく、MissingDrawerプラグインを使用して、サイドバーにディレクトリ構造を表示させています。)
まずGemfile
を見てみましょう。中身は以下のとおりです。
source 'http://rubygems.org' gem 'rails', '3.1.0.beta1' # Bundle edge Rails instead: # gem 'rails', :git => 'git://github.com/rails/rails.git' gem 'sqlite3' # Asset template engines gem 'sass' gem 'coffee-script' gem 'uglifier' gem 'jquery-rails' # Use unicorn as the web server # gem 'unicorn' # Deploy with Capistrano # gem 'capistrano' # To use debugger # gem 'ruby-debug19', :require => 'ruby-debug' group :test do # Pretty printed test output gem 'turn', :require => false end
最初の数行には特に驚くような部分はないですが、そのあとに“Asset template engines”というセクションが登場します。RailsはこのバージョンからSASSとCoffeeScriptがデフォルトで有効になりました。これについては今後のエピソードでより詳しく見ていきます。このセクションには、アプリケーションのproductionモードでのJavaScriptコードを縮小化するUglifierというgemと、jquery-rails gemが含まれています。Prototypeを使用している場合は、これの代わりにprototype-railsになります。
ファイルの末尾には、turn gemへの参照情報があります。これはテスト出力をきれいにするためのgemで、後ほど説明します。
テンプレートの変更点
ここでの最も大きな変更点は、テンプレートエンジンによるassetsの扱いです。アプリケーションの/public
ディレクトリを開くと、javascripts
とstylesheets
ディレクトリがなくなっています。
もしこれらのディレクトリがなくなったのなら、JavaScriptとCSSのコードをどこに置けばいいのでしょうか。その答ですが、それらは新しい/app/assets
ディレクトリに移動しました。javascripts
ディレクトリを開くと、application.js
ファイルがあります。しかし中を見てみると、ここはアプリケーションのJavascriptを置く場所ではないことがわかります。
// FIXME: Tell people that this is a manifest file, real code should go into discrete files // FIXME: Tell people how Sprockets and CoffeeScript works // //= require jquery //= require jquery_ujs //= require_tree .
このファイルは登録簿的に使用されるよう設計されています。ここにJavascriptコードを直接書かずに、同じディレクトリ内の他のファイルに記述します。これがどのように動くかを理解するために、アプリケーションでProject
のためのscaffoldを生成します。
$ rails g scaffold project name:string
このコマンドからの出力を見てみると、app/assets
ディレクトリにいくつかのファイルが生成されたのがわかります。
create app/assets/stylesheets/scaffold.css.scss invoke assets create app/assets/javascripts/projects.js.coffee create app/assets/stylesheets/projects.css.scss
生成されたファイルの中にはCSSファイルがありますが拡張子は.scss
になっています。またJavascriptファイルの拡張子は.coffee
になっています。この後すぐにこれらのファイルを見ていきますが、その前にデータベースをマイグレートして、新たにprojects
テーブルを作成します。
$ rake db:migrate
生成されたSCSSファイルはぱっと見たところは標準的なCSSと同じように見えますが、最後の方を見ると、ネストされたルールなどいくつか違いがあるのがわかります。これはSASS固有の機能で、今後のエピソードでSASSを紹介するときに詳しく見ていきます。
#error_explanation { width: 450px; border: 2px solid red; padding: 7px; padding-bottom: 0; margin-bottom: 20px; background-color: #f0f0f0; h2 { text-align: left; font-weight: bold; padding: 5px 5px 5px 15px; font-size: 12px; margin: -7px; margin-bottom: 0px; background-color: #c00; color: #fff; } ul li { font-size: 12px; list-style: square; } }
加えてscaffoldが生成したのは、Projects
内のページだけで使用されるCSSのためのprojects.css.scss
ファイルとjavascriptのためのprojects.js.coffee
ファイルです。これは単なる推奨であるということに注意してください。すべてのCSSとJavascriptファイルは、それぞれ一つのファイルにまとめられて、すべてのページの表示時にロードされます。
アプリケーションのサーバを起動してprojectsページを開くと、ページのソースにそれら2つのファイルが含まれているのがわかります。
<link href="/assets/application.css" media="screen" ↵ rel="stylesheet" type="text/css" /> <script src="/assets/application.js" type="text/javascript"> ↵ </script>
これら2つのファイルの中身は、アプリケーションのJavaScriptとCSSのファイルのそれぞれの統合された版で、これらのファイルの中身はapplication.js
とapplication.css
ファイルのrequire
の行に基づいています。これらの行によって、どのファイルがどういう順番で結合ファイルに追加されるかが決まります。
//= require jquery //= require jquery_ujs //= require_tree .
Railsは裏でSprocketsを使用してこれらを実現しています。SprocketsはアプリケーションのすべてのJavascriptとCSSファイルを集めて一つのファイルにしてクライアントに送ります。この仕組みによって、アプリケーション内のファイルをどのような構成で配置しても、それらが最終的に一つのファイルに結合されます。productionモードではファイルが縮小化されるので、できるかぎり効率的にダウンロードされることになります。
developmentモードではapplication.js
とapplication.css
の各ファイルは自動的に再読み込みされますが、productionモードではCoffeeScriptとSASSのファイルはコンパイルされるため、パフォーマンスの低下を防ぐためにこれらのファイルはキャッシュされます。
マイグレーションの変更点
Rails 3.1のその他の新機能を見ていきましょう。まず先ほどのscaffoldコマンドで生成されたデータベースマイグレーションファイルです。
class CreateProjects < ActiveRecord::Migration def change create_table :projects do |t| t.string :name t.timestamps end end end
このマイグレーションはこれまでのものとは違っています。通常のup
とdown
のメソッドはchange
メソッドに置き換わって、一つでupとdownを処理するようになりました。Rails 3.1では、changeメソッドから判断できる限り、2つのメソッドを書く必要がなくなりました。ActiveRecordにこの便利な機能が加わったことで、マイグレーションのコードを生成する時間が少なくなります。
Identity Map
Identity Mapは、ActiveRecordに追加されたもう一つの優れた機能です。設定ファイルapplication.rb
を見てみると、デフォルトで有効に設定されているのがわかります。
# Enable IdentityMap for Active Record, ↵ to disable set to false or remove the line below. config.active_record.identity_map = true
Rails 3.1の最初のベータ版でこの機能を動かすためには、この設定ファイルを少しいじって、コードを一行追加します。
# Enable IdentityMap for Active Record, ↵ to disable set to false or remove the line below. config.active_record.identity_map = true ActiveRecord::IdentityMap.enabled = true
IdentityMap機能によって何ができるのでしょうか? 実例で説明するためにコンソールで使用します。最初に新規のProject
を作成します。
> p = Project.create!(:name => "Yardwork") SQL (81.4ms) INSERT INTO "projects" ("created_at", "name", "updated_at") VALUES (?, ?, ?) [["created_at", Fri, 13 May 2011 18:41:25 UTC +00:00], ["name", "Yardwork"], ["updated_at", Fri, 13 May 2011 18:41:25 UTC +00:00]] => #<Project id: 1, name: "Yardwork", created_at: "2011-05-13 18:41:25", updated_at: "2011-05-13 18:41:25">
まずここではログが直接コンソールに表示されるので、コマンドが実行されたときにどのようなSQLが発行されているのかを知ることができます。
次に新しく作成したProject
をid
を指定して取り出します。
ruby-1.9.2-p180 :002 > p1 = Project.find(1) Project Loaded From Identity Map (id: 1) => #<Project id: 1, name: "Yardwork", created_at: "2011-05-13 18:41:25", updated_at: "2011-05-13 18:41:25">
コンソールにはProject Loaded From Identity Map (id: 1)
と表示されています。データベースからprojectを取り出すのにSQLクエリは実行されません。そのprojectがすでにメモリにロードされていることをRailsが知っていてそのインスタンスを使用するからです。これを確認するために両方のProjectのobject_id
が同じかどうかをチェックします。
ruby-1.9.2-p180 :006 > p.object_id == p1.object_id => true
つまり、あるレコードについて属性を設定しながら、それがアソシエーションを介して読み込んだレコードと同一であると考えて問題がないということです。それが同じインスタンスであることが保証されているからです。
ネストされたhas_many :throughアソシエーション
他にもいくつかActiveRecordに加えられた機能がありますが、その中のひとつがネストされたhas_many :through
アソシエーションです。次のモデルのコードを見てください。
class Project < ActiveRecord::Base has_many :tasks has_many :assignments, :through => :tasks has_many :users, :through => :assignments end
Railsの以前のバージョンではこのようなことはできませんでした。3.1ではhas_many :through
アソシエーションをネストすることができるようになったので、役に立つ場面が多いでしょう。
もう一つの新機能として、attr_accessible
呼び出しにロールを割り当てることができるようになりました。例えば、name
属性をアクセス可能に設定するときに、adminロールを持つユーザだけに限定できるようになりました。
class Project < ActiveRecord::Base attr_accessible :name, :as => :admin end
コンソールで作成したProject
に対して、ブラウザから項目名を「庭仕事(Yardwork)」から「家事(Housework)」に変更しようとしても、adminとしてログインしていないため変更できません。
:as
オプションを付けてupdate_attributes
の呼び出しを修正して、コントローラを介してロールを渡します。
def update @project = Project.find(params[:id]) respond_to do |format| if @project.update_attributes(params[:project], :as => :admin) format.html { redirect_to @project, notice: 'Project ↵ was successfully updated.' } format.json { head :ok } else format.html { render action: "edit" } format.json { render json: @project.errors, ↵ status: :unprocessable_entity } end end end
フォームからProject
を更新しようとすると、今度はadminロールを持っているので成功します。
ビュー層の変更点
最後にビュー層の変更点を紹介します。一つ目は小さいですが便利な機能です。従来はフォームにfile_field
がある場合、アップロードされるファイルが正しく送信されるようにform_for
に:html => { :multipart => :true }
を追加する必要がありました。Rails 3.1ではその必要はなくなり、フォームにfile_field
が含まれる場合はform
タグに適切なenctype
属性を加える形になりました。
もう一つはリンクの変更です。URLヘルパーにドメインとサブドメインオプションを渡せるようになりました。例えばリンクとして次のようなコードがあるとします。
<%= link_to 'Edit', edit_project_path(@project) %>
この編集画面へのリンクで別のサブドメインを指定したい場合、このコードを次のように変えることができます。
<%= link_to 'Edit', edit_project_url(@project, ↵ :subdomain => 'foo') %>
これでリンクはfoo
サブドメインの同じページを指定できます。
見やすくなったテスト出力
最後に、改善されたテスト出力を見てみます。rake test
を実行すると、前にGemfile
で見たturn gemを使うことで出力が整理されて見やすくなりました。
$ rake test (in /Users/eifion/todo) Loaded suite /Users/eifion/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake/rake_test_loader Started Finished in 0.003966 seconds. 0 tests, 0 assertions, 0 failures, 0 errors, 0 skips Loaded suite /Users/eifion/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake/rake_test_loader Started ProjectsControllerTest: PASS should create project (0.16s) PASS should destroy project (0.01s) PASS should get edit (0.08s) PASS should get index (0.01s) PASS should get new (0.01s) PASS should show project (0.01s) PASS should update project (0.01s) Finished in 0.307078 seconds. 7 tests, 10 assertions, 0 failures, 0 errors, 0 skips
Rails 3.1にはまだここで触れることができなかった新機能がたくさんあります。自動ストリーミング、ビューの継承、マウント可能なエンジン、その他の新機能を、今後のエピソードで紹介していきます。
Rails 3.1での変更点の完全なリストは、こちらの概要のページに掲載されているので参照してください。