1 Infrastructure
Rails 2.2는 Rails를 원활하게 유지하고 세상의 나머지 부분과 연결되도록 하는 인프라 측면에서 중요한 릴리스입니다.
1.1 Internationalization
Rails 2.2는 internationalization(또는 타이핑이 지루한 분들을 위한 i18n)을 위한 간단한 시스템을 제공합니다.
- 주요 기여자: Rails i18 팀
- 추가 정보:
1.2 Ruby 1.9와 JRuby와의 호환성
스레드 안전성과 함께, Rails가 JRuby 및 곧 출시될 Ruby 1.9와 잘 작동하도록 많은 작업이 이루어졌습니다. Ruby 1.9가 계속 변화하는 목표물이기 때문에, 최신 Rails를 최신 Ruby에서 실행하는 것은 여전히 성공할지 실패할지 모르는 상황이지만, Rails는 Ruby 1.9가 출시될 때 전환할 준비가 되어 있습니다.
2 Documentation
코드 주석 형태의 Rails 내부 문서가 수많은 부분에서 개선되었습니다. 또한, Ruby on Rails Guides 프로젝트는 주요 Rails 컴포넌트에 대한 정보의 결정판입니다. 첫 공식 릴리스에서 Guides 페이지는 다음을 포함합니다:
- Rails 시작하기
- Rails 데이터베이스 마이그레이션
- Active Record 관계
- Active Record 쿼리 인터페이스
- Rails의 레이아웃과 렌더링
- Action View 폼 헬퍼
- 외부에서 본 Rails 라우팅
- Action Controller 개요
- Rails 캐싱
- Rails 애플리케이션 테스팅 가이드
- Rails 애플리케이션 보안
- Rails 애플리케이션 디버깅
- Rails 플러그인 제작 기초
전체적으로, 가이드는 초급 및 중급 Rails 개발자들을 위한 수만 단어의 지침을 제공합니다.
애플리케이션 내에서 이러한 가이드를 로컬에서 생성하고 싶다면:
$ rake doc:guides
이렇게 하면 가이드가 Rails.root/doc/guides
안에 위치하게 되며, 선호하는 브라우저에서 Rails.root/doc/guides/index.html
을 열어 바로 탐색을 시작할 수 있습니다.
- 주요 기여자: Xavier Noria와 Hongli Lai
- 추가 정보:
3 HTTP와의 향상된 통합 : 기본 제공되는 ETag 지원
HTTP 헤더에서 ETag와 마지막 수정 타임스탬프를 지원한다는 것은 최근에 수정되지 않은 리소스에 대한 요청을 받았을 때 Rails가 빈 응답을 보낼 수 있다는 것을 의미합니다. 이를 통해 응답을 보내야 하는지 여부를 확인할 수 있습니다.
class ArticlesController < ApplicationController
def show_with_respond_to_block
@article = Article.find(params[:id])
# 요청이 stale?에 제공된 옵션과 다른 헤더를 보내는 경우,
# 요청은 실제로 stale하며 respond_to 블록이 실행됩니다
# (그리고 stale? 호출의 옵션이 응답에 설정됩니다).
#
# 요청 헤더가 일치하면 요청이 fresh하고 respond_to 블록이
# 실행되지 않습니다. 대신 기본 render가 발생하는데, 이는 last-modified와
# etag 헤더를 확인하고 템플릿을 렌더링하는 대신 "304 Not Modified"만
# 보내면 된다고 결론을 내립니다.
if stale?(:last_modified => @article.published_at.utc, :etag => @article)
respond_to do |wants|
# 일반적인 응답 처리
end
end
end
def show_with_implied_render
@article = Article.find(params[:id])
# 응답 헤더를 설정하고 요청에 대해 확인합니다. 요청이 stale인 경우
# (즉, etag나 last-modified가 일치하지 않는 경우) 기본 템플릿 렌더링이 발생합니다.
# 요청이 fresh한 경우, 기본 render는 템플릿을 렌더링하는 대신
# "304 Not Modified"를 반환합니다.
fresh_when(:last_modified => @article.published_at.utc, :etag => @article)
end
end
4 Thread Safety
Rails 2.2에서 Rails를 thread-safe하게 만드는 작업이 진행되었습니다. 웹 서버 인프라에 따라 메모리에 더 적은 수의 Rails 인스턴스로 더 많은 요청을 처리할 수 있으며, 이는 더 나은 서버 성능과 다중 코어의 더 높은 활용으로 이어집니다.
애플리케이션의 production 모드에서 멀티스레드 디스패칭을 활성화하려면 config/environments/production.rb
에 다음 줄을 추가하세요:
config.threadsafe!
애플리케이션이 thread-safe 하다고 선언하여 모든 dependency loading을 부트 타임으로 이동합니다.
5 Active Record
여기서 이야기할 두 가지 중요한 추가 사항이 있습니다: transactional migrations과 pooled database transactions입니다. 또한 join 테이블 조건을 위한 새롭고 (더 깔끔한) 문법과 함께 여러 가지 작은 개선사항들이 있습니다.
5.1 Transactional Migrations
과거에는 여러 단계의 Rails migration이 문제의 원인이었습니다. migration 도중 문제가 발생하면 오류 발생 전까지의 모든 변경사항은 데이터베이스에 적용되었고, 오류 이후의 내용은 적용되지 않았습니다. 또한 migration 버전이 이미 실행된 것으로 저장되어, 문제를 해결한 후에도 rake db:migrate:redo
로 단순히 재실행할 수 없었습니다. Transactional migration은 migration 단계들을 DDL 트랜잭션으로 감싸서 이 문제를 해결합니다. 따라서 어느 단계에서든 실패하면 전체 migration이 취소됩니다. Rails 2.2에서는 PostgreSQL에서 기본적으로 transactional migration을 지원합니다. 이 코드는 향후 다른 데이터베이스 유형으로 확장 가능하며, IBM은 이미 DB2 adapter를 지원하도록 확장했습니다.
- Lead Contributor: Adam Wiggins
- 추가 정보:
5.2 Connection Pooling
Connection pooling은 Rails가 데이터베이스 요청을 데이터베이스 connection pool에 분산시킬 수 있게 해줍니다. 이 pool은 최대 크기까지 증가할 수 있습니다(기본값은 5이지만, database.yml
에 pool
키를 추가하여 조정할 수 있습니다). 이는 많은 동시 사용자를 지원하는 애플리케이션의 병목 현상을 제거하는 데 도움이 됩니다. 또한 포기하기 전까지 기다리는 시간인 wait_timeout
이 있으며 기본값은 5초입니다. 필요한 경우 ActiveRecord::Base.connection_pool
을 통해 pool에 직접 접근할 수 있습니다.
development:
adapter: mysql
username: root
database: sample_development
pool: 10
wait_timeout: 10
- 리드 기여자: Nick Sieger
- 추가 정보:
5.3 Join Table 조건을 위한 Hash
이제 hash를 사용하여 join table에 대한 조건을 지정할 수 있습니다. 복잡한 join을 통해 쿼리를 수행해야 하는 경우에 매우 유용합니다.
class Photo < ActiveRecord::Base
belongs_to :product
end
class Product < ActiveRecord::Base
has_many :photos
end
# 저작권이 없는 사진을 가진 모든 product를 가져오기:
Product.all(:joins => :photos, :conditions => { :photos => { :copyright => false }})
5.4 새로운 Dynamic Finders
Active Record의 dynamic finders 그룹에 두 가지 새로운 메소드가 추가되었습니다.
5.4.1 find_last_by_attribute
find_last_by_attribute
메소드는 Model.last(:conditions => {:attribute => value})
와 동일합니다.
# London에서 가입한 마지막 유저를 가져옴
User.find_last_by_city('London')
- 주요 기여자: Emilio Tagua
5.4.2 find_by_attribute!
새로운 bang! 버전인 find_by_attribute!
는 Model.first(:conditions => {:attribute => value}) || raise ActiveRecord::RecordNotFound
와 동일합니다. 일치하는 레코드를 찾을 수 없을 때 nil
을 반환하는 대신, 이 메서드는 일치하는 것을 찾을 수 없을 경우 exception을 발생시킵니다.
# Moby가 아직 가입하지 않았다면 ActiveRecord::RecordNotFound 예외를 발생시킵니다!
User.find_by_name!('Moby')
- 주요 기여자: Josh Susser
5.5 Associations에서 Private/Protected Scope 존중
Active Record association 프록시는 이제 프록시된 객체의 메서드 스코프를 존중합니다. 이전에는 (User가 has_one :account를 가지고 있다고 가정할 때) @user.account.private_method
가 연관된 Account 객체의 private 메서드를 호출했습니다. Rails 2.2에서는 이것이 실패합니다. 만약 이 기능이 필요하다면 @user.account.send(:private_method)
를 사용해야 합니다(또는 메서드를 private이나 protected 대신 public으로 만들어야 합니다). 만약 method_missing
을 오버라이드하고 있다면, association이 정상적으로 작동하도록 respond_to
도 일치하는 동작으로 오버라이드해야 한다는 점에 주의하세요.
- 주요 기여자: Adam Milligan
- 추가 정보:
5.6 기타 Active Record 변경사항
rake db:migrate:redo
는 이제 재실행할 특정 버전을 지정하는 옵션인 VERSION을 받습니다- UTC 타임스탬프 대신 숫자 접두사로 된 마이그레이션을 사용하려면
config.active_record.timestamped_migrations = false
를 설정하세요. - Counter cache 컬럼(
:counter_cache => true
로 선언된 관계)은 더 이상 0으로 초기화할 필요가 없습니다. - 모델 이름의 국제화를 지원하는 사람이 읽기 쉬운 번역을 위한
ActiveRecord::Base.human_name
6 Action Controller
컨트롤러 측면에서는 라우트를 정리하는 데 도움이 되는 몇 가지 변경사항이 있습니다. 또한 복잡한 애플리케이션의 메모리 사용량을 줄이기 위한 라우팅 엔진의 내부 변경사항도 있습니다.
6.1 Shallow Route Nesting
Shallow route nesting은 깊이 중첩된 resource를 사용할 때 발생하는 잘 알려진 어려움에 대한 해결책을 제공합니다. Shallow nesting을 사용하면 작업하고자 하는 resource를 고유하게 식별하는데 필요한 정보만 제공하면 됩니다.
map.resources :publishers, :shallow => true do |publisher|
publisher.resources :magazines do |magazine|
magazine.resources :photos
end
end
위 코드는 다음 경로들을 생성합니다:
HTTP 메서드 | 경로 | action | 사용 목적 |
---|---|---|---|
GET | /publishers | index | publishers 목록 표시 |
GET | /publishers/new | new | publisher 생성 양식 표시 |
POST | /publishers | create | publisher 생성 |
GET | /publishers/:id | show | 특정 publisher 표시 |
GET | /publishers/:id/edit | edit | publisher 수정 양식 표시 |
PATCH/PUT | /publishers/:id | update | 특정 publisher 수정 |
DELETE | /publishers/:id | destroy | 특정 publisher 삭제 |
GET | /publishers/:publisher_id/magazines | index | publisher의 magazines 목록 표시 |
POST | /publishers/:publisher_id/magazines | create | publisher의 magazine 생성 |
GET | /publishers/:publisher_id/magazines/new | new | publisher의 magazine 생성 양식 표시 |
GET | /magazines/:id | show | 특정 magazine 표시 |
GET | /magazines/:id/edit | edit | magazine 수정 양식 표시 |
PATCH/PUT | /magazines/:id | update | 특정 magazine 수정 |
DELETE | /magazines/:id | destroy | 특정 magazine 삭제 |
GET | /magazines/:magazine_id/photos | index | magazine의 photos 목록 표시 |
POST | /magazines/:magazine_id/photos | create | magazine의 photo 생성 |
GET | /magazines/:magazine_id/photos/new | new | magazine의 photo 생성 양식 표시 |
GET | /photos/:id | show | 특정 photo 표시 |
GET | /photos/:id/edit | edit | photo 수정 양식 표시 |
PATCH/PUT | /photos/:id | update | 특정 photo 수정 |
DELETE | /photos/:id | destroy | 특정 photo 삭제 |
이렇게 하면 (다른 것들과 함께) 다음과 같은 라우트들이 인식됩니다:
/publishers/1 ==> publisher_path(1) # 출판사
/publishers/1/magazines ==> publisher_magazines_path(1) # 출판사의 잡지들
/magazines/2 ==> magazine_path(2) # 잡지
/magazines/2/photos ==> magazines_photos_path(2) # 잡지의 사진들
/photos/3 ==> photo_path(3) # 사진
- 리드 컨트리뷰터: S. Brent Faulkner
- 추가 정보:
6.2 Member 또는 Collection 라우트를 위한 Method 배열
이제 새로운 member나 collection 라우트에 method 배열을 전달할 수 있게 되었습니다. 하나 이상의 method를 처리해야 할 필요가 있을 때마다 모든 verb를 허용하는 라우트를 정의해야 하는 귀찮음이 사라졌습니다. Rails 2.2에서는 다음과 같은 라우트 선언이 가능합니다:
map.resources :photos, :collection => { :search => [:get, :post] }
photo 리소스에 검색을 위한 collection route를 추가합니다. GET과 POST 둘 다 허용됩니다.
- 리드 컨트리뷰터: Brennan Dunn
6.3 특정 Action을 가진 Resources
기본적으로 map.resources
를 사용하여 route를 생성할 때, Rails는 7개의 기본 action(index, show, create, new, edit, update, destroy)에 대한 route를 생성합니다. 하지만 이러한 각각의 route는 애플리케이션의 메모리를 차지하고, Rails가 추가적인 routing 로직을 생성하게 만듭니다. 이제 :only
와 :except
옵션을 사용하여 Rails가 resource에 대해 생성할 route를 세밀하게 조정할 수 있습니다. 단일 action, action 배열, 또는 특별한 :all
이나 :none
옵션을 제공할 수 있습니다. 이러한 옵션들은 중첩된 resource들에 의해 상속됩니다.
map.resources :photos, :only => [:index, :show]
# :only 옵션으로 인덱스와 상세 보기 액션만 생성됩니다
map.resources :products, :except => :destroy
# :except 옵션으로 destroy 액션을 제외한 모든 액션이 생성됩니다
- 리드 컨트리뷰터: Tom Stuart
6.4 기타 Action Controller의 변경사항
- 이제 request를 라우팅하는 동안 발생한 예외에 대해 커스텀 에러 페이지를 쉽게 표시할 수 있습니다.
- HTTP Accept header는 이제 기본적으로 비활성화되었습니다. 원하는 format을 지정할 때는 formatted URL(
/customers/1.xml
과 같은)을 사용하는 것이 좋습니다. Accept header가 필요한 경우config.action_controller.use_accept_header = true
로 다시 활성화할 수 있습니다. - Benchmarking 수치가 이제 작은 초 단위 분수가 아닌 밀리초 단위로 표시됩니다.
- Rails는 이제 HTTP-only cookies를 지원하며(세션에도 사용됨), 이는 최신 브라우저에서 일부 cross-site scripting 위험을 완화하는데 도움이 됩니다.
redirect_to
는 이제 URI schemes를 완벽하게 지원합니다(예를 들어, svn`ssh: URI로 리다이렉트할 수 있습니다).render
는 이제 적절한 MIME type으로 일반 JavaScript를 렌더링하기 위한:js
옵션을 지원합니다.- Request forgery 보호가 강화되어 HTML-formatted content request에만 적용됩니다.
- Polymorphic URL은 전달된 파라미터가 nil인 경우 더 합리적으로 동작합니다. 예를 들어, nil date로
polymorphic_path([@project, @date, @area])
를 호출하면project_area_path
가 반환됩니다.
7 Action View
javascript_include_tag
와stylesheet_link_tag
는:all
과 함께 사용할 수 있는 새로운:recursive
옵션을 지원하므로, 한 줄의 코드로 전체 파일 트리를 로드할 수 있습니다.- 포함된 Prototype JavaScript 라이브러리가 버전 1.6.0.3으로 업그레이드되었습니다.
RJS#page.reload
로 JavaScript를 통해 브라우저의 현재 위치를 새로고침할 수 있습니다.atom_feed
헬퍼는 이제 XML 처리 명령어를 삽입할 수 있는:instruct
옵션을 제공합니다.
8 Action Mailer
Action Mailer는 이제 mailer layouts을 지원합니다. 적절한 이름의 레이아웃을 제공하여 HTML 이메일을 브라우저 내 뷰만큼 아름답게 만들 수 있습니다 - 예를 들어, CustomerMailer
클래스는 layouts/customer_mailer.html.erb
를 사용합니다.
Action Mailer는 이제 STARTTLS를 자동으로 활성화하여 GMail의 SMTP 서버에 대한 기본 지원을 제공합니다. 이는 Ruby 1.8.7이 설치되어 있어야 합니다.
9 Active Support
Active Support는 이제 Rails 애플리케이션을 위한 내장 memoization, each_with_object
메서드, delegates의 prefix 지원 및 다양한 새로운 유틸리티 메서드를 제공합니다.
9.1 Memoization
Memoization은 메서드를 한 번 초기화한 다음 그 값을 반복 사용을 위해 저장해두는 패턴입니다. 이 패턴을 자신의 애플리케이션에서 사용해본 적이 있을 것입니다:
def full_name
@full_name ||= "#{first_name} #{last_name}"
end
이 코드는 instance variable @full_name을 사용하여 메모이제이션을 수행합니다. 만약 @full_name이 nil인 경우에만 새로운 문자열 연결을 실행하고, 그 외에는 기존에 저장된 값을 반환합니다. first_name과 last_name을 연결하여 전체 이름을 생성합니다.
Memoization을 사용하면 이 작업을 선언적인 방식으로 처리할 수 있습니다:
extend ActiveSupport::Memoizable
def full_name
"#{first_name} #{last_name}"
end
memoize :full_name
ActiveSupport::Memoizable
를 extend하고 메서드 정의 후에 memoize
호출을 하여 메서드의 결과를 메모화합니다.
memoization의 다른 기능으로는 memoization을 켜고 끌 수 있는 unmemoize
, unmemoize_all
, memoize_all
이 있습니다.
- 주요 기여자: Josh Peek
- 추가 정보:
9.2 each_with_object
each_with_object
메서드는 Ruby 1.9에서 백포트된 메서드를 사용하여 inject
의 대안을 제공합니다. 이는 컬렉션을 순회하면서 현재 요소와 메모를 블록으로 전달합니다.
%w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase } # => {'foo' => 'FOO', 'bar' => 'BAR'}
메인 기여자: Adam Keys
9.3 Prefix를 사용한 Delegate
한 클래스에서 다른 클래스로 동작을 delegate할 때, delegate된 메서드를 식별하기 위한 prefix를 지정할 수 있습니다. 예를 들어:
class Vendor < ActiveRecord::Base
has_one :account
delegate :email, :password, to: :account, prefix: true
end
이는 위임된 메소드 vendor#account_email
과 vendor#account_password
를 생성합니다. custom prefix도 지정할 수 있습니다:
class Vendor < ActiveRecord::Base
has_one :account
# account로의 email과 password를 owner_email, owner_password로 위임합니다
delegate :email, :password, :to => :account, :prefix => :owner
end
이는 vendor#owner_email
과 vendor#owner_password
의 위임 메서드를 생성할 것입니다.
Lead Contributor: Daniel Schierbeck
9.4 기타 Active Support 변경사항
- Ruby 1.9 호환성 수정을 포함한
ActiveSupport::Multibyte
의 광범위한 업데이트. ActiveSupport::Rescuable
추가로 모든 클래스에서rescue_from
문법을 믹스인할 수 있게 됨.- 날짜/시간 비교를 용이하게 하기 위해
Date
와Time
클래스에past?
,today?
,future?
추가. Array#[1]
부터Array#[4]
까지의 별칭으로Array#second
부터Array#fifth
까지 추가.collection.size > 1
을 캡슐화하는Enumerable#many?
추가.to_param
에서 사용할 수 있도록 URL에 적합한 버전을 생성하는Inflector#parameterize
추가.Time#advance
가 일수와 주수의 소수점을 인식하여1.7.weeks.ago
,1.5.hours.since
등이 가능해짐.- 포함된 TzInfo 라이브러리가 버전 0.3.12로 업그레이드됨.
ActiveSupport::StringInquirer
는 문자열의 동등성을 테스트하는 멋진 방법을 제공:ActiveSupport::StringInquirer.new("abc").abc? => true
10 Railties
Railties(Rails 자체의 핵심 코드)에서 가장 큰 변경사항은 config.gems
메커니즘에 있습니다.
10.1 config.gems
Rails 애플리케이션을 더 자급자족적으로 만들고 배포 이슈를 피하기 위해, Rails 애플리케이션이 필요로 하는 모든 gem의 복사본을 /vendor/gems
에 저장할 수 있습니다. 이 기능은 Rails 2.1에서 처음 등장했지만, Rails 2.2에서는 gem들 간의 복잡한 의존성을 처리하는 데 있어 더욱 유연하고 강력해졌습니다. Rails의 gem 관리에는 다음과 같은 명령어들이 포함됩니다:
config/environment.rb
파일에config.gem _gem_name_
추가rake gems
로 설정된 모든 gem들의 목록을 확인하고, 해당 gem들(및 의존성)이 설치되었는지, frozen 상태인지, framework인지 확인 (framework gem들은 gem 의존성 코드가 실행되기 전에 Rails에 의해 로드되는 것들입니다; 이러한 gem들은 frozen될 수 없습니다)rake gems:install
로 누락된 gem들을 컴퓨터에 설치rake gems:unpack
으로 필요한 gem들의 복사본을/vendor/gems
에 저장rake gems:unpack:dependencies
로 필요한 gem들과 그 의존성들의 복사본을/vendor/gems
에 저장rake gems:build
로 누락된 native extension들을 빌드rake gems:refresh_specs
로 Rails 2.1에서 생성된 vendored gem들을 Rails 2.2 방식의 저장 방식으로 정렬
명령줄에서 GEM=_gem_name_
을 지정하여 단일 gem을 unpack하거나 설치할 수 있습니다.
- Lead Contributor: Matt Jones
- 더 자세한 정보:
10.2 기타 Railties 변경사항
- Thin 웹 서버의 팬이라면,
script/server
가 이제 Thin을 직접 지원한다는 소식에 기뻐하실 것입니다. script/plugin install <plugin> -r <revision>
는 이제 git 기반과 svn 기반 플러그인 모두에서 작동합니다.script/console
이 이제--debugger
옵션을 지원합니다- Rails 자체를 빌드하기 위한 continuous integration 서버 설정 지침이 Rails 소스에 포함되어 있습니다
rake notes:custom ANNOTATION=MYFLAG
를 사용하면 사용자 정의 주석을 나열할 수 있습니다.Rails.env
를StringInquirer
로 감싸서Rails.env.development?
와 같이 사용할 수 있습니다- 더 이상 사용되지 않는다는 경고를 제거하고 gem 의존성을 적절히 처리하기 위해 Rails는 이제 rubygems 1.3.1 이상이 필요합니다.
11 Deprecated
이번 릴리스에서 몇 가지 오래된 코드가 더 이상 사용되지 않습니다:
Rails::SecretKeyGenerator
가ActiveSupport::SecureRandom
으로 대체되었습니다render_component
는 더 이상 사용되지 않습니다. 이 기능이 필요한 경우 render_components 플러그인을 사용할 수 있습니다.partial 렌더링 시 암시적 지역 변수 할당이 더 이상 사용되지 않습니다.
def partial_with_implicit_local_assignment @customer = Customer.new("Marcel") render :partial => "customer" end
이전에는 위 코드가 'customer' partial 내에서
customer
라는 지역 변수를 사용할 수 있게 했습니다. 이제는 :locals 해시를 통해 모든 변수를 명시적으로 전달해야 합니다.country_select
가 제거되었습니다. 자세한 정보와 대체 플러그인은 deprecation 페이지를 참조하세요.ActiveRecord::Base.allow_concurrency
는 더 이상 아무 효과가 없습니다.ActiveRecord::Errors.default_error_messages
는I18n.translate('activerecord.errors.messages')
를 선호하여 더 이상 사용되지 않습니다국제화를 위한
%s
와%d
보간 구문은 더 이상 사용되지 않습니다.String#chars
는String#mb_chars
를 선호하여 더 이상 사용되지 않습니다.분수 개월이나 분수 년의 기간은 더 이상 사용되지 않습니다. 대신 Ruby의 core
Date
와Time
클래스 연산을 사용하세요.Request#relative_url_root
는 더 이상 사용되지 않습니다. 대신ActionController::Base.relative_url_root
를 사용하세요.
12 Credits
릴리스 노트 작성: Mike Gunderloy