読者です 読者をやめる 読者になる 読者になる

おもしろwebサービス開発日記

Ruby や Rails を中心に、web技術について書いています

Rails 3.0 リリースノート和訳 - 後編

rails

お待たせしました。後編です。一週間で書くつもりが一ヶ月かかりました><

Rails 3.0 リリースノート和訳 - 前編 - おもしろWEBサービス開発日記

7 Action Pack

Action Packには重要な変更が(内部的にも外部的にも)あるよ。

7.1 Abstract Controller

Abstract Controller は Action Controller の一般的な部分を取り出して他のライブラリでも使えるモジュールにしたものだよ。これによって他のライブラリで

  • render template
  • render partials
  • helpers
  • translations
  • logging
  • リクエストがきてレスポンスを返すサイクル全般

ができるようになったよ。この抽象化によって ActionMailer::Base が AbstractControllerを継承して Mail gem に Rails DSLを単にラップしたものになったよ。

また抽象化の過程でAction Controllerのソースが綺麗になったよ。

Abstract Controller は ユーザが叩けるAPIは提供してないよ。普通にRailsを使ってる限りはAbstract Controllerのことを意識することはないよ。

詳しくはこちら

Rails Edge Architecture

7.2 Action Controller

cookie関連の修正

注意: この章は意訳やid:willnetの個人的な追記部分が強めです

が追加されたよ。

下記はCHANGE LOGの抜粋です。

cookies.permanent[:prefers_open_id] = true
# => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
cookies.signed[:discount] = 45 # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
cookies.signed[:discount] # => 45 (if the cookie was changed, you'll get a InvalidSignature exception)
cookies.permanent.signed[:remember_me] = current_user.id
# => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT

上記のCHANGE LOGを見るとわかるように、

  • cookies.parmanentを使うとexpiresが20年後に設定される
  • cookies.signedを使うとcookieが暗号化され、署名が付けられる
  • cookies.signed利用時に改ざん検知するとInvalidSignature
  • cookies.parmanent.signedとすることで両方同時に使える

となるようです。また、cookies.signedの署名に使われる鍵は initializers/cookie_verification_secret.rb で設定されています

参考

Commit 0200e20f148c96afceeebc4da7b5985643f9f707 to rails's rails - GitHub
has_many :bugs, :through => :rails: Signed and Permanent cookies in Rails 3

config 関連の修正

session_store の設定は initializers/session_store.rb に移動したよ

respond 関連の修正

:notice => 'This is a flash message' や :alert => 'Something went wrong'が respond_to ブロックの中の format call に渡せるようになったよ。これまでのflash[]もまだ使えるよ。

たぶんこういう感じで使うはず・・(ぐぐっても情報無かったので間違ってたらすいません><)

class UsersController < ApplicationController
  def index
    @users = User.all
    respond_to do |format|
      format.html(:notice => 'This is a flash message')
      # format.html(:alert => 'Something went wrong')
      format.xml { render :xml => @users }
      format.json { render :json => @users }
    end
  end
end

respond_with メソッドがControllerに追加されたよ。 これでこれまで下記のように書いていたのが

class UsersController < ApplicationController
  def index
    @users = User.all
    respond_to do |format|
      format.html
      format.xml { render :xml => @users }
      format.json { render :json => @users }
    end
  end
end

下記のように書けるようになってすっきりするよ。

class UsersController < ApplicationController
  respond_to :html, :xml, :json
  def index
    @users = User.all
    respond_with(@users)
  end
end


新しく追加された ActionController::Responder を使うと responseを作るのが楽になるよ

その他

application_controller.rb は protect_from_forgery がデフォルトでonだよ

class ApplicationController < ActionController::Base
  protect_from_forgery
end

rails3.0のapplication_controller.rbやたらすっきりしましたね

非推奨になったもの

パスワードなどそのままログに出力すると問題のある文字列を"[FILTERED]"のように置換してくれるメソッド filter_parameter_logging が config.filter_parametters << :password になったよ

詳しくはこちら

Render Options in Rails 3
Three reasons to love ActionController::Responder

7.3 Action Dispatch

Action DispatchはRails3.0で新しく提供された、きれいなルーティング用の実装だよ。

railsのルーティング用の実装を書き直した結果、rack_mountというRack用のスタンドアローンなソフトウェアにRailsDSLを乗っけたものになったよ。

それぞれのアプリのルーティングの定義はアプリケーションのモジュールの中の名前空間で分けられるよ。つまりもともと下記のように書いてたのが

ActionController::Routing::Routes.draw do |map|
  map.resources :posts
end

こんな風に書けるようになったよ

AppName::Application.routes do
  resources :posts
end

ルーティング定義用に match メソッドが追加されたよ。このメソッドで、マッチしたルートに対してRackアプリを割り当てることができるよ。→下記のように書くと/homeにリクエストを投げたときにHomeApp(Sinatra)を動かすことが出来るみたいです。

class HomeApp < Sinatra::Base
  get "/" do
    "Hello World!"
  end
end

Basecamp::Application.routes do
  match "/home", :to => HomeApp
end

matchメソッドを使った通常のRailsのルーティングは下記のような感じになるようです。

Basecamp::Application.routes do
  match "/main/:id", :to => "main#home" # => MainController#homeが実行される
end

ルーティング定義用に constraints メソッドが追加されたよ。制限を定義してルーティングを守ることが出来るよ。→下記のように、constraintsメソッドでsubdomainのルーティングが出来るみたいです。

Basecamp::Application.routes do
  constraints(:subdomain => "support") do
    match "/foo/bar", :to => "foo#bar"
  end
end

scopeメソッドが追加されたよ。異なる言語やaction用に名前空間ルーティングを定義できるよ。

scopeメソッドの例

scope 'es' do
  resources :projects, :path_names => { :edit => 'cambiar' }, :as => 'projeto'
end

# edit アクションは /es/projeto/1/cambiar として定義される

root メソッドが match '/', :to => pathの省略形として追加されたよ

matchメソッドの中でオプションのパスを設定できるようになったよ。下記の例で括弧でくくられた部分はオプションになるよ。

match "/:controller(/:action(/:id))(.:format)"


ルーティングはブロック経由でも表現できるよ。

例: 下記の二つは同じ。インラインでも書けるし

match "/:action", :to => "main"

ブロックでも書ける

controller :home {
  match ‘/:action’
}


これまでのmapメソッドを使ったやり方はまだ使えるけど3.1では出来なくなるよ。

非推奨なもの

  • RESTでないルーティング(/:controller/:action/:id)はコメントアウトしたよ
  • :path_prefixは無くなったよ。
  • :name_prefixは自動的に"_"が最後につくようになったよ。

詳しくはこちら

The Rails 3 Router: Rack it Up « Katz Got Your Tongue?
Revamped Routes in Rails 3 | Rizwan Reza
Generic Actions in Rails 3 « Katz Got Your Tongue?

7.4 Action View

Action Viewのヘルパーで主に書き直されたのは、Unobtrusive JavaScript(UJS)のフックの実装と古いインラインAJAXメソッドを削除したとこ。これによりヘルパー内でUJSのドライバーが使えるようになったよ。

ちなみにUnobtrusive JavaScriptとは、簡単に言うと"HTMLからJavaScriptを分離してHTMLを綺麗に保つ"みたいな考え方です。

remote_ なヘルパーはRailsコアから削除してprototype_legacy_helperに入れたよ。UJSのフックが欲しいときには

form_for @post, :remote => true

のようにする。そうすると下記のようなHTMLが出力されるよ。

<form action="http://host.com" id="create-post" method="post" data-remote="true">

上記HTML中の"data-remote"はHTML5のカスタム属性で、"true"に設定されているとAJAXでリクエストをすることを示すようです。設定しておけばあとはブラウザが勝手にリクエストを投げてくれる訳ではなく、実際の動作はJavaScriptで定義することになるようです(たぶん)


HTMLの出力をエスケープするために、これまではhメソッドを使ってたけど、hメソッドがデフォルトになったので明示的に使う必要はなくなったよ。エスケープしたくないときにはraw()メソッドを使うといいよ。

ヘルパーの出力はデフォルトでHTML5だよ

label ヘルパーは引数が一つの時I18nで翻訳した結果を返すよ。f.label :nameだったら :name の翻訳を出力するよ

:en.support.select が :en.helpers.selectに変わったよ

ERbの改行を削除するために<% -%>の"-"をする必要がなくなったよ

grouped_collection_select ヘルパーが追加されたよ(訳注:2.3.5にも同名のヘルパーメソッドがあるようなのですが・・・何か変更されたんでしょうかね?)

javascript_include_tag と stylesheet_include_tagで宣言されたファイルが見つからない時は例外を出すよ

content_for? メソッドが追加されて、viewを描画する前にcontentが存在するかチェックするよ。

8 Active Model

Active ModelはRails3.0で新しく登場したよ。Active Modelのインターフェースを使うことで、RailsでいろんなORMのライブラリが使えるようになるよ。

8.1 ORM の抽象化と Action Pack のインターフェース

疎結合の結果 Active Record と Action Pack は完全に切り離されたよ。全てのORMプラグインはActive Model のインターフェースだけ叩けば Action Pack ともシームレスに動くよ。

詳しくはこちら

ActiveModel: Make Any Ruby Object Feel Like ActiveRecord

8.2 Validations

validationsはActive RecordからActive Modelに移されたよ。ORMライブラリのインターフェースを提供するよ。

validates :attributes options_hash という既存の validates_xxx 系のメソッドのショートカットとなるメソッドが追加されたよ。オプション引数は下記のようなものがとれるよ

key value
:acceptance boolean
:confirmation boolean
:exclusion {:in => Enumerable }
:inclusion {:in => Enumerable }
:format { :with => Regexp, :on => :create}
:length {:maximum => Fixnum}
:numericality boolean
:presence boolean
:uniqueness boolean

rails 2.3スタイルのvalidationメソッドは3.0でも使えるよ。validatesメソッドはあくまで新しく追加されたメソッドで、これまでのメソッドを置き換えるものではないよ。

validatorオブジェクトはActive Modelを使うオブジェクト間で再利用できるよ

class TitleValidator < ActiveModel::EachValidator
  Titles = ['Mr.', 'Mrs.', 'Dr.']
  def validate_each(record, attribute, value)
  unless Titles.include?(value)
    record.errors[attribute] << 'must be a valid title'
    end
  end
end

上記のような TitleValidator クラスを定義すると、下記のように ActiveModel::Validations を includeしているクラスで validatesメソッドを :title => true の引数付きで実行したときに、TitleValidatorクラスのvalidate_eachメソッドが実行されるようです。

class Person
  include ActiveModel::Validations
  attr_accessor :title
  validates :title, :presence => true, :title => true
end 

# Active Recordの場合はこう書ける
class Person < ActiveRecord::Base
  validates :title, :presence => true, :title => true
end

これにより複数クラス間でcustom validationを使い回すことが出来ます。


詳しくはこちら

Sexy Validation in Rails 3
Rails 3 Validations Explained

9 Active Record

Active Record は Rails 3.0 で、 Active Modelに抽象性を移行させたのと、クエリのインタフェースをArelを使って更新させたこと、validation のアップデートと多くの修正とエンハンスで注目の的だよ。全てのRails 2.x のAPIは3.1まで互換性をサポートされるよ。

9.1 Query Interface

Arelを使ってるActive Recordは、コアメソッドを使うと関連を返すよ。Rails 2.3.xのAPIはまだサポートされて、3.1までは非推奨にはならないよ。でも 3.2では削除されるよ。新しいAPIは、下記のメソッドを提供するよ。これらのメソッドは関連を返してメソッドチェーンできるよ。メソッドの説明は名前の通りなので省略します。

  • where
  • select
  • group
  • having
  • joins
  • order
  • limit
  • from
  • clause
  • includes
  • lock
  • readonly
  • scope
  • with_scope
  • default_scope

詳しくはこちら

Active Record Query Interface
Let your SQL Growl in Rails 3

9.2 Enhancements

9.3 Patches and Deprecations

Active Recordはたくさん修正されてるよ

  • SQLite2のサポートが無くなったよ。SQLite3使ってね。
  • MySQLがcolumn orderをサポートするよ。
  • PostgreSQL adapterはTIME ZONEサポートを修正して、不正確な値が入らないようになったよ。
  • PostgreSQLでテーブル名に複数スキーマをサポートするようになったよ。
  • PostgreSQLXML data型のカラムをサポートするようになったよ
  • table_nameがキャッシュされるよ
  • Oracle adapterがいろいろ修正されてたくさんバグが直ってるよ

named_scopeはscopeに名前が変わったよ。scopeメソッドは下記のような感じで使うよ。

scope :since, lambda { |time|
  where("created_at > ?", time)
}

save(false)が非推奨になって、save(:validate => false)になったよ

ActiveRecordI18nでのエラーメッセージの定義はen.activerecord.errors.templateからen.errors.templateに変わったよ。

model.errors.onは非推奨になって、model.errors[]になったよ

validates_presence_of がvalidates ... :presence => trueになったよ

ログに色を付けてくれる設定の ActiveRecord::Base.colorize_logging と config.active_record.colorize_logging が非推奨になって、Rails::Subscriber.colorize_logging か config.colorize_logging に変わったよ

Stete Machineの実装が数ヶ月されてたけど、3.0の時点では削除されているよ。

10 Active Recource

Acrive Recource もまたActive ResourceオブジェクトをAction Packでシームレスに使うために ActiveModel に切り出されたよ。

validations は Active Model に追加されたよ

  • observing hooksが追加されたよ
  • HTTP proxyをサポートするよ
  • digest autehticationのサポートを追加したよ
  • modelの名前をActive Modelに移動したよ
  • 通常のアクセスでのActive Recourceの属性をHashに変更したよ
  • first, last, allメソッドを追加したよ
  • find_every は何も返さないときにResourceNotFoundエラーを返さなくなったよ
  • save! オブジェクトが追加されたよ。オブジェクトがvalid?でfalseになるときに RecourceInvalidになるよ。
  • Active Recource modelにupdate_attribute とupdate_attributesが追加されたよ
  • exists?メソッドが追加されたよ
  • SchemaDefinitionがSchemaに変更されたよ
  • define_schemaがschemaに変更されたよ
  • load errorの時に、content-typeを使うのではなくてActive Recourceのformatメソッドを使ってね
  • schemaブロックではinstance_evalを使ってね
  • @responseがcodeメソッドかmessageメソッドを持たない場合のActiveResource::ConnectionError#to_sを修正してRuby1.9互換にしたよ。
  • JSONフォーマットのエラーサポートを追加したよ
  • loadメソッドが数値の配列を確実に返すようにしたよ
  • リソースが削除されたときに410のレスポンスを返すよ
  • Active Recourceの接続にSSLオプションが追加できるようになったよ
  • connection timeoutの設定がNet::HTTPのopen_timeoutに影響するよ

非推奨になったもの

  • save(false)がsave(:validate => false)になったよ
  • Ruby 1.9.2のURI.parseと.decodeが使えなくなったよ

11 Active Support

Active Supportは超がんばったおかげで好きな部分を使えるようになったよ。つまりActive Supportライブラリの一部を使いたいときに、Active Supportを全部requireする必要がないってことだよ。これによりRailsコアコンポーネントをスリムに出来るよ。

Active Supportの主な変更は下記だよ

  • ライブラリ中の使ってないメソッドを削除したよ
  • Active SupportはもうTZInfo, Memcache ClientとBuilderを提供しないよ。これらは全て独立していて、bundle installでインストールするよ。
  • ActiveSupport::SafeBufferにsafe bufferが実装されたよ
  • Array.uniq_by とArray.uniq_by!が追加されたよ
  • TimeZone.seconds_to_utc_offsetが間違った値を返すバグを修正したよ
  • ActiveSupport::Notifications middlewareを追加したよ
  • ActiveSupport.use_standard_json_time_formatがデフォルトでtrueだよ
  • ActiveSupport.escape_html_entities_in_jsonがデフォルトでfalseだよ
  • Intenger#multiple_of? が0を引数として受け取って、レシーバが0出なければfalseを返すよ
  • string.charsがstring.mb_charsに変わったよ
  • ActiveSupport::OrderdHashはYAMLをデシリアライズできるよ
  • XmlMini用のSAXベースのパーサを追加したよ。LibXMLとNokogiriを使ってるよ。
  • Object#presenceが追加されたよ。object#present?がtrueだったらobjectを返してそうでなければnilを返すよ。
  • String#exclude?が追加されたよ。include?の逆を返すよ。
  • DateTimeにto_iが追加されたので、modelのDateTime属性でto_yamlが正しく動くよ。
  • Enumerable#exclude?が追加されたよ。Enumerable#include?の逆だよ
  • XSSエスケープがデフォルトに変わったよ
  • ActiveSupport::HashWithIndifferentAccessで深いmergeをサポートしたよ
  • Enumerable#sumが全てのenumerableで(sizeが無くても)動くようになったよ。
  • 0.seconds などの 0の時間の長さのオブジェクトのinspectメソッドは空文字列でなくて'0 seconds'を返すよ。
  • Modelnameのメソッドとしてelementとcollectionが追加されたよ(:例 Post.model_name.collection # => "posts")
  • String#to_timeとString#to_datetimeがコンマ秒以下を扱うよ。
  • beforeメソッドとafterメソッドを定義している around filter オブジェクトのための新しいコールバックをサポートするよ。
  • ActiveSupport::OrderdHash#to_aメソッドが整列された配列を返すよ。Ruby1.9のHash#to_aに合わせているよ。
  • MissingSourceFileは定数として存在するけど今はLoadErrorと同じだよ。
  • クラスレベルの属性の値をサブクラスで継承/オーバライド可能にするためにClass#class_attributeが追加されたよ。
  • ActiveRecord::Associations中のDeprecatedCallbackがついに削除されたよ。

下記のメソッドRuby1.8.7や1.9で使えるので削除されたよ

  • Integer#even? と Integer#odd?
  • String#each_char
  • String#start_with? と String#end_with? (starts_with? と ends_with?はそのまま使えるよ)
  • String#bytesize
  • Object#tap
  • Symbol#to_proc
  • Object#instance_variable_defined?
  • Enumerable#none?

REXMLのセキュリティパッチはActive Supportに残ってるよ。理由は若いpatchlevelのRuby 1.8.7にはまだ必要だからだよ。Active Supportはそれが適用される必要があるか無いかを知ってるよ。

下記のメソッドは使う必要がもう無いので削除されたよ。

  • Kernel#daemonize
  • Object#remove_subclasses_of Object#extend_with_included_modules_from, Object#extended_by
  • Class#remove_class
  • Regexp#number_of_captures, Regexp.unoptionalize, Regexp.optionalize, Regexp#number_of_captures

12 Action Mailer

Action Mailer は 利用するライブラリが TMail から Mail に変わって、新しいAPIになったよ。Action Mailerはほぼ全部書き換えられたよ。結果としてAction Mailerは単純にAbstract Controllerを継承して、Mail gemにRails DSLをラップしたものになったよ。これで他のライブラリとの重複が減ってコードの量が減ったよ。

  • 全てのmailerはデフォルトでapp/mailersに配置されるよ。
  • メール送信に新しい三つのメソッドが使えるよ。attachments, headers, mailの三つ。
  • Action Mailer のメールメソッドは Mail::Message オブジェクトを返すようになったよ。このオブジェクトは deliver メソッドで自分自身を送るよ。
  • 全ての送信系のメソッドは抽象化されて Mail gem へ移されてるよ
  • 全ての送信系のメソッドはvalidなメールヘッダと値のハッシュを受け取るよ。
  • 送信系のメソッドはAction Controllerのrespond_toと同じような方法で動くよ。明示的でも暗黙的でもtemplatesが使えるよ。Action Mailerは必要に応じてmultipart emailになるよ。
  • mail ブロックの中で、format.mime_typeを使ったり、render でテキストを指定したりlayoutsやtemplateを指定したり出来るよ。procの中にrenderが書けるのはAbstract Controllerからきていて、サポートしてるoptionも同じだよ。
  • mailerのunit testはfunctional testに移動したよ。

非推奨になったもの

  • :charset, :content_type, :mime_version, :implicit_parts_order は全て非推奨で、ActionMailer.default :key => value のスタイルがが推奨だよ。
  • create_メソッド名 と deliver_メソッド名 は非推奨で、単に メソッド名 を使ってMail::Messageオブジェクトを取得するようにするのが推奨だよ。
  • ActionMailer.deliver(message)は非推奨で、message.deliverが推奨だよ。
  • template_root は非推奨で、mailを生成するブロック中のformat.mime_typeメソッドに渡すブロック中のrenderにオプションを渡すのが推奨だよ。
  • インスタンス変数をbodyメソッド中で使う形 (body {:ivar => value} の形) は非推奨になったよ。インスタンス変数はメソッド中で直接宣言すればviewの中で使えるようになったよ。
  • Mailerは app/models から app/mailersに移行したよ。

詳しくはこちら

New Action Mailer API in Rails 3
New Mail Gem for Ruby