homeASCIIcasts

265: Rails 3.1の概要 

(view original Railscast)

Other translations: En Es

Other formats:

Written by Naomi Fujimoto

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プラグインを使用して、サイドバーにディレクトリ構造を表示させています。)

TextMateで表示させたRails 3.1のディレクトリ構造

まずGemfileを見てみましょう。中身は以下のとおりです。

/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はこのバージョンからSASSCoffeeScriptがデフォルトで有効になりました。これについては今後のエピソードでより詳しく見ていきます。このセクションには、アプリケーションのproductionモードでのJavaScriptコードを縮小化するUglifierというgemと、jquery-rails gemが含まれています。Prototypeを使用している場合は、これの代わりにprototype-railsになります。

ファイルの末尾には、turn gemへの参照情報があります。これはテスト出力をきれいにするためのgemで、後ほど説明します。

テンプレートの変更点

ここでの最も大きな変更点は、テンプレートエンジンによるassetsの扱いです。アプリケーションの/publicディレクトリを開くと、javascriptsstylesheetsディレクトリがなくなっています。

publicディレクトリにjavascriptsとstylesheetsディレクトリがない

もしこれらのディレクトリがなくなったのなら、JavaScriptとCSSのコードをどこに置けばいいのでしょうか。その答ですが、それらは新しい/app/assetsディレクトリに移動しました。javascriptsディレクトリを開くと、application.jsファイルがあります。しかし中を見てみると、ここはアプリケーションのJavascriptを置く場所ではないことがわかります。

/app/assets/javascripts/application.js

// 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を紹介するときに詳しく見ていきます。

/app/assets/stylesheets/scaffold.css.scss

#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.jsapplication.cssファイルのrequireの行に基づいています。これらの行によって、どのファイルがどういう順番で結合ファイルに追加されるかが決まります。

/app/assets/javascripts/application.js

//= require jquery
//= require jquery_ujs
//= require_tree .

Railsは裏でSprocketsを使用してこれらを実現しています。SprocketsはアプリケーションのすべてのJavascriptとCSSファイルを集めて一つのファイルにしてクライアントに送ります。この仕組みによって、アプリケーション内のファイルをどのような構成で配置しても、それらが最終的に一つのファイルに結合されます。productionモードではファイルが縮小化されるので、できるかぎり効率的にダウンロードされることになります。

developmentモードではapplication.jsapplication.cssの各ファイルは自動的に再読み込みされますが、productionモードではCoffeeScriptとSASSのファイルはコンパイルされるため、パフォーマンスの低下を防ぐためにこれらのファイルはキャッシュされます。

マイグレーションの変更点

Rails 3.1のその他の新機能を見ていきましょう。まず先ほどのscaffoldコマンドで生成されたデータベースマイグレーションファイルです。

/db/migrate/20110511193808_create_projects.rb

class CreateProjects < ActiveRecord::Migration
  def change
    create_table :projects do |t|
      t.string :name

      t.timestamps
    end
  end
end

このマイグレーションはこれまでのものとは違っています。通常のupdownのメソッドはchangeメソッドに置き換わって、一つでupとdownを処理するようになりました。Rails 3.1では、changeメソッドから判断できる限り、2つのメソッドを書く必要がなくなりました。ActiveRecordにこの便利な機能が加わったことで、マイグレーションのコードを生成する時間が少なくなります。

Identity Map

Identity Mapは、ActiveRecordに追加されたもう一つの優れた機能です。設定ファイルapplication.rbを見てみると、デフォルトで有効に設定されているのがわかります。

/config/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の最初のベータ版でこの機能を動かすためには、この設定ファイルを少しいじって、コードを一行追加します。

/config/application.rb

# 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が発行されているのかを知ることができます。

次に新しく作成したProjectidを指定して取り出します。

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アソシエーションです。次のモデルのコードを見てください。

/app/models/project.rb

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ロールを持つユーザだけに限定できるようになりました。

/app/models/project.rb

class Project < ActiveRecord::Base
  attr_accessible :name, :as => :admin
end

コンソールで作成したProjectに対して、ブラウザから項目名を「庭仕事(Yardwork)」から「家事(Housework)」に変更しようとしても、adminとしてログインしていないため変更できません。

適切なロールをもっていないので変更できない

:asオプションを付けてupdate_attributesの呼び出しを修正して、コントローラを介してロールを渡します。

/app/controllers/projects_controller.rb

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での変更点の完全なリストは、こちらの概要のページに掲載されているので参照してください。