1 애플리케이션 아키텍처
Rails 애플리케이션의 아키텍처에는 두 가지 주요 변경 사항이 있습니다: 모듈식 웹 서버 인터페이스인 Rack의 완전한 통합과 Rails Engines에 대한 지원 개선입니다.
1.1 Rack 통합
Rails는 이제 CGI의 과거와 결별하고, 모든 곳에서 Rack을 사용합니다. 이는 많은 내부적인 변화를 필요로 했고 또 그러한 변화를 가져왔습니다(하지만 CGI를 사용하더라도 걱정하지 마세요; Rails는 이제 프록시 인터페이스를 통해 CGI를 지원합니다). 여전히, 이는 Rails 내부의 주요한 변화입니다. 2.3 버전으로 업그레이드한 후에는 로컬 환경과 프로덕션 환경에서 테스트해야 합니다. 테스트해야 할 사항들:
- Sessions
- Cookies
- File uploads
- JSON/XML APIs
다음은 rack 관련 변경사항들의 요약입니다:
script/server
는 Rack을 사용하도록 변경되었으며, 이는 모든 Rack 호환 서버를 지원한다는 의미입니다.script/server
는 또한 rackup 설정 파일이 존재하면 이를 사용합니다. 기본적으로config.ru
파일을 찾지만,-c
스위치로 이를 재정의할 수 있습니다.- FCGI 핸들러는 Rack을 통해 작동합니다.
ActionController::Dispatcher
는 자체 기본 middleware 스택을 관리합니다. Middleware들은 주입되거나, 재정렬되거나, 제거될 수 있습니다. 스택은 부팅 시 체인으로 컴파일됩니다.environment.rb
에서 middleware 스택을 구성할 수 있습니다.- middleware 스택을 검사하기 위한
rake middleware
태스크가 추가되었습니다. 이는 middleware 스택의 순서를 디버깅하는 데 유용합니다. - 통합 테스트 러너가 전체 middleware와 애플리케이션 스택을 실행하도록 수정되었습니다. 이로 인해 통합 테스트는 Rack middleware를 테스트하는 데 완벽합니다.
ActionController::CGIHandler
는 Rack을 감싸는 하위 호환성을 가진 CGI 래퍼입니다.CGIHandler
는 기존 CGI 객체를 가져와서 그 환경 정보를 Rack 호환 형식으로 변환하는 것을 목적으로 합니다.CgiRequest
와CgiResponse
가 제거되었습니다.- Session 저장소는 이제 지연 로딩됩니다. 요청 중에 session 객체에 접근하지 않으면, session 데이터를 로드하지 않습니다(쿠키 파싱, memcache에서 데이터 로드, Active Record 객체 조회).
- 테스트에서 쿠키 값을 설정할 때 더 이상
CGI::Cookie.new
를 사용할 필요가 없습니다. request.cookies["foo"]에String
값을 할당하면 예상대로 쿠키가 설정됩니다. CGI::Session::CookieStore
가ActionController::Session::CookieStore
로 대체되었습니다.CGI::Session::MemCacheStore
가ActionController::Session::MemCacheStore
로 대체되었습니다.CGI::Session::ActiveRecordStore
가ActiveRecord::SessionStore
로 대체되었습니다.ActionController::Base.session_store = :active_record_store
로 여전히 session 저장소를 변경할 수 있습니다.- 기본 sessions 옵션은 여전히
ActionController::Base.session = { :key => "..." }
로 설정됩니다. 하지만,:session_domain
옵션이:domain
으로 이름이 변경되었습니다. - 일반적으로 전체 요청을 감싸는 mutex가
ActionController::Lock
middleware로 이동되었습니다. ActionController::AbstractRequest
와ActionController::Request
가 통합되었습니다. 새로운ActionController::Request
는Rack::Request
를 상속합니다. 이는 테스트 요청에서response.headers['type']
접근에 영향을 미칩니다. 대신response.content_type
을 사용하세요.ActiveRecord
가 로드되었다면ActiveRecord::QueryCache
middleware가 자동으로 middleware 스택에 삽입됩니다. 이 middleware는 요청별 Active Record 쿼리 캐시를 설정하고 초기화합니다.- Rails 라우터와 컨트롤러 클래스들은 Rack 스펙을 따릅니다.
SomeController.call(env)
로 컨트롤러를 직접 호출할 수 있습니다. 라우터는 라우팅 파라미터를rack.routing_args
에 저장합니다. ActionController::Request
는Rack::Request
를 상속합니다.config.action_controller.session = { :session_key => 'foo', ...
대신config.action_controller.session = { :key => 'foo', ...
를 사용하세요.ParamsParser
middleware를 사용하면 모든 XML, JSON, YAML 요청을 전처리하여 그 이후의 모든Rack::Request
객체에서 정상적으로 읽을 수 있습니다.
1.2 Rails Engines에 대한 지원 재개
몇 버전 동안의 업그레이드가 없었던 후, Rails 2.3은 Rails Engines (다른 애플리케이션에 내장될 수 있는 Rails 애플리케이션)에 대해 몇 가지 새로운 기능을 제공합니다. 첫째, engines의 라우팅 파일은 이제 routes.rb
파일처럼 자동으로 로드되고 다시 로드됩니다 (이는 다른 plugins의 라우팅 파일에도 적용됩니다). 둘째, 만약 플러그인이 app 폴더를 가지고 있다면, app/[models|controllers|helpers]가 자동으로 Rails load path에 추가됩니다. Engines는 이제 view paths 추가도 지원하며, Action Mailer와 Action View는 engines와 다른 plugins의 view를 사용합니다.
2 Documentation
Ruby on Rails guides 프로젝트는 Rails 2.3을 위한 몇 가지 추가 가이드를 발행했습니다. 또한, 별도의 사이트에서 Edge Rails를 위한 업데이트된 가이드 사본을 관리하고 있습니다. 다른 문서화 노력으로는 Rails wiki의 재출시와 Rails Book을 위한 초기 계획이 포함됩니다.
- 추가 정보: Rails Documentation Projects
3 Ruby 1.9.1 지원
Rails 2.3은 Ruby 1.8이나 이제 출시된 Ruby 1.9.1에서 실행하든 모든 자체 테스트를 통과해야 합니다. 하지만 1.9.1로 이동하는 것은 Rails core뿐만 아니라 의존하는 모든 data adapters, plugins 및 기타 코드의 Ruby 1.9.1 호환성을 확인해야 한다는 점을 알아야 합니다.
4 Active Record
Rails 2.3에서 Active Record는 많은 새로운 기능과 버그 수정을 받았습니다. 주요 내용으로는 nested attributes, nested transactions, dynamic 및 default scopes, batch processing이 포함됩니다.
4.1 Nested Attributes
Active Record는 이제 중첩된 모델의 속성을 직접 업데이트할 수 있습니다. 단, 이를 하도록 명시적으로 지정해야 합니다:
class Book < ActiveRecord::Base
has_one :author
has_many :pages
accepts_nested_attributes_for :author, :pages
# :author 및 :pages의 nested attributes를 허용
end
nested attributes를 활성화하면 다음과 같은 여러 기능이 가능해집니다: 연관된 자식 레코드들과 함께 자동으로(그리고 원자적으로) 레코드를 저장하고, 자식을 인식하는 유효성 검증, 그리고 nested forms 지원(이후에 설명)을 할 수 있습니다.
:reject_if
옵션을 사용하여 nested attributes를 통해 추가되는 새로운 레코드의 요구사항을 지정할 수도 있습니다:
accepts_nested_attributes_for :author,
:reject_if => proc { |attributes| attributes['name'].blank? }
name
attribute가 비어있을 경우 reject하는 author의 nested attribute를 허용합니다.
- Lead Contributor: Eloy Duran
- 추가 정보: Nested Model Forms
4.2 Nested Transactions
Active Record는 이제 많은 사용자들이 요청했던 기능인 nested transactions를 지원합니다. 이제 다음과 같은 코드를 작성할 수 있습니다:
User.transaction do
User.create(:username => 'Admin')
User.transaction(:requires_new => true) do
User.create(:username => 'Regular')
raise ActiveRecord::Rollback
end
end
User.find(:all) # => Admin만 반환됨
중첩 트랜잭션을 사용하면 외부 트랜잭션의 상태에 영향을 주지 않고 내부 트랜잭션을 롤백할 수 있습니다. 트랜잭션을 중첩하려면 :requires_new
옵션을 명시적으로 추가해야 합니다. 그렇지 않으면 중첩된 트랜잭션은 단순히 상위 트랜잭션의 일부가 됩니다(현재 Rails 2.2에서와 같이). 내부적으로 중첩 트랜잭션은 savepoint를 사용하므로 실제 중첩 트랜잭션을 지원하지 않는 데이터베이스에서도 지원됩니다. 또한 테스트 중에 이러한 트랜잭션이 transactional fixtures와 잘 작동하도록 하는 약간의 마법이 있습니다.
- 주요 기여자: Jonathan Viney와 Hongli Lai
4.3 동적 Scope
Rails에는 동적 finder(find_by_color_and_flavor
와 같은 메서드를 즉석에서 만들 수 있음)와 named scope(재사용 가능한 쿼리 조건을 currently_active
와 같은 친숙한 이름으로 캡슐화할 수 있음)가 있습니다. 이제 동적 scope 메서드를 사용할 수 있습니다. 즉석에서 필터링과 메서드 체이닝을 함께 사용할 수 있는 문법을 만드는 것이 핵심입니다. 예를 들면:
Order.scoped_by_customer_id(12)
Order.scoped_by_customer_id(12).find(:all,
:conditions => "status = 'open'")
Order.scoped_by_customer_id(12).scoped_by_status("open")
위 구문들은 customer_id
가 12인 주문들과 상태가 'open'인 주문들에 대해 scoped finder를 적용한 예시입니다.
동적 scope를 사용하기 위해 별도로 정의할 것은 없습니다: 그냥 동작합니다.
- Lead Contributor: Yaroslav Markin
- More Information: What's New in Edge Rails: Dynamic Scope Methods
4.4 Default Scopes
Rails 2.3은 named scope와 유사한 default scopes 개념을 도입합니다. 하지만 이는 모델 내의 모든 named scope나 find 메서드에 적용됩니다. 예를 들어, default_scope :order => 'name ASC'
를 작성하면 해당 모델에서 레코드를 조회할 때마다 이름순으로 정렬됩니다(물론 옵션을 재정의하지 않는 한).
- 주요 기여자: Paweł Kondzior
- 추가 정보: What's New in Edge Rails: Default Scoping
4.5 Batch Processing
find_in_batches
를 사용하여 Active Record 모델의 많은 수의 레코드를 메모리에 대한 부담을 줄이면서 처리할 수 있습니다:
Customer.find_in_batches(:conditions => {:active => true}) do |customer_group|
customer_group.each { |customer| customer.update_account_balance! }
end
활성화된 customer들의 계정 잔액을 일괄 업데이트하는 방법을 보여주는 예제입니다.
find
의 대부분의 옵션들을 find_in_batches
에 전달할 수 있습니다. 하지만 레코드가 반환되는 순서를 지정할 수는 없으며(항상 정수여야 하는 primary key의 오름차순으로 반환됩니다), :limit
옵션을 사용할 수 없습니다. 대신 기본값이 1000인 :batch_size
옵션을 사용하여 각 batch에서 반환될 레코드의 수를 설정할 수 있습니다.
새로운 find_each
메서드는 find_in_batches
를 래핑하여 개별 레코드를 반환하며, find 자체는 batch로 수행됩니다(기본값은 1000개).
Customer.find_each do |customer|
customer.update_account_balance!
end
이 방식은 레코드를 배치(batch)로 검색한 다음, 각 레코드를 개별적으로 yield합니다. 위 예제에서는 배치(batch) 단위로 Customer 레코드 집합을 가져와서 고객당 하나씩 block으로 yield합니다.
이 메서드는 batch 처리를 위해서만 사용해야 합니다. 적은 수의 레코드(1000개 미만)의 경우에는 일반적인 find 메서드를 자신의 루프와 함께 사용해야 합니다.
- 추가 정보 (당시에는 이 convenience 메서드를 단순히
each
라고 불렀습니다):
4.6 Callback에 대한 다중 조건
Active Record callback을 사용할 때, 동일한 callback에서 :if
와 :unless
옵션을 조합할 수 있으며, 배열로 여러 조건을 제공할 수 있습니다:
before_save :update_credit_rating, :if => :active,
:unless => [:admin, :cash_only]
:if
와 :unless
조건부를 함께 사용할 때는 위와 같이 :if
가 true이고 :unless
가 false일 때만 콜백이 실행됩니다. :admin
과 :cash_only
둘 다 false이면서 :active
가 true일 때만 신용등급이 갱신됩니다.
- 주요 기여자: L. Caviola
4.7 having을 사용한 Find
이제 Rails는 grouped finds에서 레코드를 필터링하기 위한 :having
옵션을 find(그리고 has_many
와 has_and_belongs_to_many
associations에도)에서 지원합니다. SQL에 익숙한 사람들이 알고 있듯이, 이는 grouped 결과를 기반으로 필터링할 수 있게 해줍니다:
developers = Developer.find(:all, :group => "salary",
:having => "sum(salary) > 10000", :select => "salary")
salary를 기준으로 그룹화하고, 그룹당 salary의 합이 10000을 초과하는 developer들의 salary를 찾습니다.
- 리드 기여자: Emilio Tagua
4.8 MySQL 연결 재접속하기
MySQL은 연결에서 재접속 플래그를 지원합니다 - true로 설정하면, 연결이 끊어진 경우 클라이언트가 포기하기 전에 서버에 재접속을 시도합니다. Rails 애플리케이션에서 이러한 동작을 얻으려면 database.yml
의 MySQL 연결에 reconnect = true
를 설정할 수 있습니다. 기본값은 false
이므로 기존 애플리케이션의 동작은 변경되지 않습니다.
- 리드 컨트리뷰터: Dov Murik
- 추가 정보:
4.9 기타 Active Record 변경사항
has_and_belongs_to_many
preloading에서 생성된 SQL에서 불필요한AS
가 제거되어 일부 데이터베이스에서 더 잘 동작하게 되었습니다.ActiveRecord::Base#new_record?
는 이제 기존 레코드를 만났을 때nil
대신false
를 반환합니다.- 일부
has_many :through
관계에서 테이블 이름을 인용하는 버그가 수정되었습니다. - 이제
updated_at
타임스탬프에 특정 시간을 지정할 수 있습니다:cust = Customer.create(:name => "ABC Industries", :updated_at => 1.day.ago)
find_by_attribute!
호출 실패 시 더 나은 에러 메시지를 제공합니다.- Active Record의
to_xml
지원이:camelize
옵션 추가로 더욱 유연해졌습니다. before_update
나before_create
에서 콜백을 취소할 때의 버그가 수정되었습니다.- JDBC를 통한 데이터베이스 테스트를 위한 Rake 태스크가 추가되었습니다.
validates_length_of
는:in
이나:within
옵션과 함께 사용자 정의 에러 메시지를 사용할 수 있습니다(제공된 경우).- 스코프된 select에서의 카운트가 이제 제대로 작동하여
Account.scoped(:select => "DISTINCT credit_limit").count
와 같은 작업을 할 수 있습니다. ActiveRecord::Base#invalid?
는 이제ActiveRecord::Base#valid?
의 반대로 작동합니다.
5 Action Controller
이번 릴리스에서 Action Controller는 렌더링에 중요한 변화를 적용했으며, 라우팅과 다른 영역에서도 개선되었습니다.
5.1 Unified Rendering
ActionController::Base#render
는 무엇을 렌더링할지 결정하는 데 있어 더욱 스마트해졌습니다. 이제 무엇을 렌더링할지만 알려주면 올바른 결과를 얻을 수 있습니다. 이전 버전의 Rails에서는 렌더링을 위해 명시적인 정보를 제공해야 하는 경우가 많았습니다:
render :file => '/tmp/random_file.erb' # 파일시스템의 특정 파일을 렌더링
render :template => 'other_controller/action' # 다른 컨트롤러의 템플릿을 렌더링
render :action => 'show' # 현재 컨트롤러의 다른 action 템플릿을 렌더링
Rails 2.3에서는 렌더링하려는 것을 직접 제공할 수 있습니다:
render '/tmp/random_file.erb' # 서버의 파일 시스템에서 template를 찾아 렌더링
render 'other_controller/action' # 다른 controller의 view template을 렌더링
render 'show' # 현재 controller의 show template을 렌더링
render :show # 'show'와 동일하게 동작
Rails는 렌더링될 대상에 선행 슬래시가 있는지, 중간에 슬래시가 있는지, 또는 슬래시가 전혀 없는지에 따라 file, template, action 중에서 선택합니다. action을 렌더링할 때는 문자열 대신 심볼을 사용할 수도 있습니다. 다른 렌더링 스타일(:inline
, :text
, :update
, :nothing
, :json
, :xml
, :js
)은 여전히 명시적인 옵션이 필요합니다.
5.2 Application Controller 이름 변경
application.rb
의 특별한 명명 규칙이 늘 신경 쓰였던 분들이라면 기뻐하실 일입니다! Rails 2.3에서는 application_controller.rb
로 변경되었습니다. 또한 이를 자동으로 변경해주는 새로운 rake 작업인 rake rails:update:application_controller
가 추가되었으며, 이는 일반적인 rake rails:update
프로세스의 일부로 실행됩니다.
5.3 HTTP Digest 인증 지원
Rails는 이제 HTTP digest 인증을 기본적으로 지원합니다. 이를 사용하기 위해서는 사용자의 비밀번호를 반환하는 블록과 함께 authenticate_or_request_with_http_digest
를 호출합니다(반환된 비밀번호는 해시화되어 전송된 자격 증명과 비교됩니다):
class PostsController < ApplicationController
Users = {"dhh" => "secret"}
before_filter :authenticate
def secret
render :text => "비밀번호가 필요합니다!"
end
private
def authenticate
realm = "Application"
authenticate_or_request_with_http_digest(realm) do |name|
Users[name]
end
end
end
- Lead Contributor: Gregg Kellogg
- 추가 정보: What's New in Edge Rails: HTTP Digest Authentication
5.4 더 효율적인 라우팅
Rails 2.3에서는 라우팅과 관련하여 몇 가지 중요한 변경사항이 있습니다. formatted_
route 헬퍼는 단순히 :format
을 옵션으로 전달하는 방식으로 대체되었습니다. 이는 모든 resource에 대한 route 생성 과정을 50% 줄일 수 있으며, 상당한 양의 메모리(대규모 애플리케이션에서 최대 100MB)를 절약할 수 있습니다. 만약 코드에서 formatted_
헬퍼를 사용하고 있다면 당분간은 계속 작동할 것입니다 - 하지만 이 동작은 deprecated 되었으며, 새로운 표준을 사용하여 이러한 route를 다시 작성하면 애플리케이션이 더 효율적으로 동작할 것입니다. 또 다른 큰 변화는 Rails가 이제 routes.rb
뿐만 아니라 여러 개의 라우팅 파일을 지원한다는 것입니다. RouteSet#add_configuration_file
을 사용하면 현재 로드된 route를 초기화하지 않고도 언제든지 더 많은 route를 추가할 수 있습니다. 이 변경사항은 Engine에서 가장 유용하지만, batch로 route를 로드해야 하는 모든 애플리케이션에서 사용할 수 있습니다.
- 주요 기여자: Aaron Batalion
5.5 Rack 기반 지연 로딩 세션
큰 변화로 Action Controller 세션 저장소의 기반이 Rack 레벨로 내려갔습니다. 이는 코드에서 많은 작업이 필요했지만, Rails 애플리케이션에서는 완전히 투명하게 처리됩니다(덤으로, 기존 CGI 세션 핸들러 주변의 불편한 패치들이 제거되었습니다). 하지만 한 가지 단순한 이유로 여전히 중요합니다: Rails가 아닌 Rack 애플리케이션들이 Rails 애플리케이션과 동일한 세션 저장소 핸들러(따라서 동일한 세션)에 접근할 수 있게 되었습니다. 또한, 세션은 이제 지연 로딩됩니다(프레임워크의 나머지 부분의 로딩 개선사항과 일치). 이는 세션을 원하지 않을 경우 명시적으로 비활성화할 필요가 없다는 것을 의미합니다; 단순히 세션을 참조하지 않으면 로드되지 않습니다.
5.6 MIME Type 처리 변경사항
Rails의 MIME type을 처리하는 코드에 몇 가지 변화가 있습니다. 첫째로, MIME::Type
은 이제 =~
연산자를 구현하여 동의어가 있는 type의 존재를 확인해야 할 때 더욱 깔끔해졌습니다:
if content_type && Mime::JS =~ content_type
# 멋진 작업 수행
end
Mime::JS =~ "text/javascript" => true
Mime::JS =~ "application/javascript" => true
다른 변경 사항은 프레임워크가 이제 다양한 부분에서 JavaScript를 확인할 때 Mime::JS
를 사용하여 이러한 대안들을 깔끔하게 처리한다는 것입니다.
- Lead Contributor: Seth Fitzsimmons
5.7 respond_to
의 최적화
Rails-Merb 팀 병합의 초기 결과물 중 하나로, Rails 2.3에는 respond_to
메서드에 대한 최적화가 포함되어 있습니다. respond_to
는 들어오는 request의 MIME 타입에 따라 컨트롤러가 결과를 다르게 포맷팅할 수 있도록 하는 메서드로, 많은 Rails 애플리케이션에서 널리 사용됩니다. method_missing
호출을 제거하고 프로파일링과 튜닝을 거친 결과, 세 가지 포맷을 전환하는 간단한 respond_to
에서 초당 처리할 수 있는 request 수가 8% 향상되었습니다. 가장 좋은 점은? 이러한 속도 향상을 활용하기 위해 애플리케이션 코드를 전혀 변경할 필요가 없다는 것입니다.
5.8 향상된 캐싱 성능
이제 Rails는 원격 캐시 저장소로부터의 읽기를 요청별로 로컬 캐시하여, 불필요한 읽기를 줄이고 사이트 성능을 향상시킵니다. 이 작업은 원래 MemCacheStore
에만 국한되었지만, 이제는 필요한 메서드를 구현하는 모든 원격 저장소에서 사용할 수 있습니다.
- Lead Contributor: Nahum Wild
5.9 지역화된 뷰
Rails는 이제 설정한 locale에 따라 지역화된 뷰를 제공할 수 있습니다. 예를 들어, show
액션이 있는 Posts
컨트롤러가 있다고 가정해보세요. 기본적으로 이는 app/views/posts/show.html.erb
를 렌더링할 것입니다. 하지만 I18n.locale = :da
로 설정하면, app/views/posts/show.da.html.erb
를 렌더링할 것입니다. 지역화된 템플릿이 없는 경우, 장식되지 않은 버전이 사용됩니다. Rails는 또한 현재 Rails 프로젝트에서 사용 가능한 번역의 배열을 반환하는 I18n#available_locales
와 I18n::SimpleBackend#available_locales
를 포함하고 있습니다.
추가적으로, public 디렉토리의 rescue 파일을 지역화하는 데 동일한 방식을 사용할 수 있습니다. 예를 들어, public/500.da.html
또는 public/404.en.html
이 작동합니다.
5.10 Translation에 대한 Partial Scoping
translation API의 변경으로 partial 내에서 key translation을 작성하는 것이 더 쉽고 반복이 줄어들게 되었습니다. people/index.html.erb
템플릿에서 translate(".foo")
를 호출하면, 실제로는 I18n.translate("people.index.foo")
를 호출하게 됩니다. 키 앞에 마침표를 붙이지 않으면, 이전과 마찬가지로 API는 scope를 지정하지 않습니다.
5.11 Action Controller의 기타 변경 사항
- ETag 처리가 일부 정리되었습니다: 이제 Rails는 응답 본문이 없거나
send_file
을 사용하여 파일을 전송할 때 ETag 헤더를 보내지 않습니다. - Rails의 IP 스푸핑 검사는 휴대폰으로 많은 트래픽이 있는 사이트에서는 문제가 될 수 있습니다. 프록시가 일반적으로 올바르게 설정되지 않기 때문입니다. 이런 경우라면
ActionController::Base.ip_spoofing_check = false
를 설정하여 검사를 완전히 비활성화할 수 있습니다. ActionController::Dispatcher
는 이제 자체 미들웨어 스택을 구현하며,rake middleware
를 실행하여 확인할 수 있습니다.- 쿠키 세션은 이제 서버 측 저장소와 API 호환성을 가진 영구적인 세션 식별자를 가집니다.
- 이제
send_file
과send_data
의:type
옵션에 심볼을 사용할 수 있습니다. 예:send_file("fabulous.png", :type => :png)
. map.resources
의:only
와:except
옵션은 더 이상 중첩된 리소스에 상속되지 않습니다.- 번들로 제공되는 memcached 클라이언트가 버전 1.6.4.99로 업데이트되었습니다.
expires_in
,stale?
,fresh_when
메소드는 이제 프록시 캐싱과 함께 잘 작동하도록:public
옵션을 받습니다.:requirements
옵션이 이제 추가적인 RESTful 멤버 라우트와 제대로 작동합니다.- Shallow 라우트가 이제 네임스페이스를 제대로 존중합니다.
polymorphic_url
이 불규칙한 복수형 이름을 가진 객체를 더 잘 처리합니다.
6 Action View
Rails 2.3의 Action View는 중첩된 모델 폼, render
의 개선사항, 날짜 선택 헬퍼의 더 유연한 프롬프트, 그리고 자산 캐싱의 속도 향상 등을 포함합니다.
6.1 Nested Object Forms
부모 모델이 자식 객체들의 nested attributes를 허용하는 경우(Active Record 섹션에서 설명한 대로), form_for
와 field_for
를 사용하여 nested form을 생성할 수 있습니다. 이러한 form은 임의의 깊이로 중첩될 수 있어서, 복잡한 객체 계층을 과도한 코드 없이 단일 view에서 편집할 수 있습니다. 예를 들어, 다음과 같은 모델이 있다고 가정해봅시다:
class Customer < ActiveRecord::Base
has_many :orders
# orders의 nested attributes 수정을 허용하고,
# 삭제도 가능하도록 설정합니다.
accepts_nested_attributes_for :orders, :allow_destroy => true
end
Rails 2.3에서는 다음과 같이 view를 작성할 수 있습니다:
<% form_for @customer do |customer_form| %>
<div>
<%= customer_form.label :name, '고객명:' %>
<%= customer_form.text_field :name %>
</div>
<!-- customer_form builder 인스턴스에서 fields_for를 호출합니다.
블록은 orders 컬렉션의 각 멤버에 대해 호출됩니다. -->
<% customer_form.fields_for :orders do |order_form| %>
<p>
<div>
<%= order_form.label :number, '주문번호:' %>
<%= order_form.text_field :number %>
</div>
<!-- 모델의 allow_destroy 옵션은 하위 레코드의 삭제를
가능하게 합니다. -->
<% unless order_form.object.new_record? %>
<div>
<%= order_form.label :_delete, '삭제:' %>
<%= order_form.checkbox :_delete %>
</div>
<% end %>
</p>
<% end %>
<%= customer_form.submit %>
<% end %>
- 주요 기여자: Eloy Duran
- 추가 정보:
6.2 Partial의 스마트한 렌더링
render 메서드는 수년에 걸쳐 더욱 스마트해져 왔으며, 지금은 더욱 발전했습니다. 만약 객체나 컬렉션이 있고 적절한 partial이 있으며, 이름이 일치한다면, 이제는 객체만 render하면 모든 것이 동작합니다. 예를 들어, Rails 2.3에서는 다음과 같은 render 호출이 view에서 동작합니다(적절한 이름이 지정되었다고 가정):
# render :partial => 'articles/_article',
# :object => @article와 동일
render @article
# render :partial => 'articles/_article',
# :collection => @articles와 동일
render @articles
6.3 Date Select Helper의 프롬프트
Rails 2.3에서는 collection select helper와 동일한 방식으로 다양한 date select helper(date_select
, time_select
, datetime_select
)에 사용자 정의 프롬프트를 제공할 수 있습니다. 프롬프트 문자열이나 각 컴포넌트별 개별 프롬프트 문자열의 hash를 제공할 수 있습니다. 또한 :prompt
를 true
로 설정하여 사용자 정의 일반 프롬프트를 사용할 수도 있습니다:
select_datetime(DateTime.now, :prompt => true)
select_datetime(DateTime.now, :prompt => "날짜와 시간 선택")
select_datetime(DateTime.now, :prompt =>
{:day => '일 선택', :month => '월 선택',
:year => '년 선택', :hour => '시간 선택',
:minute => '분 선택'})
- 주요 기여자: Sam Oliver
6.4 AssetTag 타임스탬프 캐싱
Rails에서 정적 asset 경로에 타임스탬프를 추가하여 "cache buster"로 사용하는 방식에 대해 잘 알고 계실 것입니다. 이는 서버에서 이미지나 stylesheet 같은 것들을 변경했을 때 사용자의 브라우저 캐시에서 오래된 복사본이 제공되는 것을 방지하는데 도움이 됩니다. 이제 Action View의 cache_asset_timestamps
설정 옵션으로 이 동작을 수정할 수 있습니다. 캐시를 활성화하면 Rails는 asset을 처음 제공할 때 한 번 타임스탬프를 계산하고 그 값을 저장합니다. 이는 정적 asset을 제공하기 위한 (비용이 많이 드는) 파일 시스템 호출을 줄일 수 있다는 의미입니다 - 하지만 서버가 실행되는 동안 asset을 수정해도 클라이언트에서 그 변경사항을 감지할 수 없다는 의미이기도 합니다.
6.5 Asset Host를 객체로 사용하기
Edge Rails에서는 asset host를 call에 응답하는 특정 객체로 선언할 수 있어 asset host가 더욱 유연해졌습니다. 이를 통해 asset hosting에 필요한 모든 복잡한 로직을 구현할 수 있습니다.
- 더 자세한 정보: asset-hosting-with-minimum-ssl
6.6 grouped_options_for_select Helper Method
Action View에는 select 컨트롤을 생성하는데 도움이 되는 많은 helper들이 이미 있었지만, 이제 하나가 더 추가되었습니다: grouped_options_for_select
. 이것은 문자열의 배열이나 해시를 받아서, optgroup
태그로 감싸진 option
태그들의 문자열로 변환합니다. 예를 들면:
grouped_options_for_select([["모자", ["야구 모자", "카우보이 모자"]]],
"카우보이 모자", "제품을 선택하세요...")
returns
<option value="">제품을 선택하세요...</option>
<optgroup label="모자">
<option value="Baseball Cap">야구 모자</option>
<option selected="selected" value="Cowboy Hat">카우보이 모자</option>
</optgroup>
6.7 Form Select Helper를 위한 비활성화된 Option 태그
form select helper들(select
와 options_for_select
같은)은 이제 :disabled
옵션을 지원하며, 이 옵션은 결과 태그에서 비활성화될 단일 값 또는 값들의 배열을 받을 수 있습니다:
select(:post, :category, Post::CATEGORIES, :disabled => 'private')
비활성화(disabled) 되어 있을 category 항목을 지정합니다. 위 예제에서는 'private' 항목이 비활성화됩니다.
returns
<select name="post[category]">
<option>story</option>
<option>joke</option>
<option>poem</option>
<option disabled="disabled">private</option>
</select>
런타임에 collections에서 어떤 option들이 선택되고/비활성화될지를 결정하기 위해 anonymous function을 사용할 수도 있습니다:
options_from_collection_for_select(@product.sizes, :name, :id, :disabled => lambda{|size| size.out_of_stock?})
재고가 없는 size를 disabled 처리하는 예시입니다.
- 주요 기여자: Tekin Suleyman
- 추가 정보: Rails 2.3의 새로운 기능 - disabled option 태그와 collection에서 option을 selecting하고 disabling하기 위한 lambda
6.8 템플릿 로딩에 대한 참고사항
Rails 2.3은 특정 environment에서 캐시된 템플릿을 활성화하거나 비활성화할 수 있는 기능을 포함하고 있습니다. 캐시된 템플릿은 렌더링될 때 새로운 템플릿 파일을 확인하지 않기 때문에 속도가 향상됩니다. 하지만 이는 서버를 재시작하지 않고는 템플릿을 "즉시" 교체할 수 없다는 것을 의미합니다.
대부분의 경우, production 환경에서는 템플릿 캐싱을 활성화하기를 원할 것입니다. 이는 production.rb
파일에서 설정을 통해 할 수 있습니다:
config.action_view.cache_template_loading = true
템플릿 로딩을 캐싱할지 여부를 결정합니다. 기본적으로는 development mode에서는 false이고 다른 환경에서는 true입니다.
Rails 2.3의 새로운 애플리케이션에서는 이 라인이 기본적으로 생성됩니다. 만약 이전 버전의 Rails에서 업그레이드했다면, Rails는 production과 test 환경에서는 template을 캐싱하지만 development 환경에서는 캐싱하지 않도록 기본 설정됩니다.
6.9 기타 Action View 변경사항
- CSRF 보호를 위한 토큰 생성이 단순화되었습니다; 이제 Rails는 세션 ID를 다루는 대신
ActiveSupport::SecureRandom
으로 생성된 단순한 무작위 문자열을 사용합니다. auto_link
가 이제 생성된 이메일 링크에 옵션(:target
과:class
등)을 제대로 적용합니다.autolink
헬퍼가 더 깔끔하고 직관적으로 리팩토링되었습니다.current_page?
가 이제 URL에 여러 쿼리 파라미터가 있는 경우에도 제대로 작동합니다.
7 Active Support
Active Support에는 Object#try
도입을 포함한 몇 가지 흥미로운 변경사항이 있습니다.
7.1 Object#try
많은 사람들이 객체에 대한 연산을 시도할 때 try()를 사용하는 방식을 채택했습니다. 특히 view에서 <%= @person.try(:name) %>
와 같은 코드를 작성하여 nil 체크를 피할 수 있어 유용합니다. 이제 이 기능이 Rails에 기본으로 포함되어 있습니다. Rails에서 구현된 대로, private 메소드에 대해서는 NoMethodError
를 발생시키며 객체가 nil인 경우 항상 nil
을 반환합니다.
- 추가 정보: try()
7.2 Object#tap Backport
Object#tap
은 Rails가 이전부터 가지고 있던 returning
메서드와 유사한 Ruby 1.9와 1.8.7의 추가 기능입니다. 블록에 yield하고 yield된 객체를 반환합니다. Rails는 이제 이전 버전의 Ruby에서도 이 기능을 사용할 수 있도록 하는 코드를 포함하고 있습니다.
7.3 XMLmini를 위한 교체 가능한 Parser
Active Support의 XML 파싱 기능은 다른 parser로 교체할 수 있도록 더 유연하게 만들어졌습니다. 기본적으로 표준 REXML 구현을 사용하지만, 적절한 gem이 설치되어 있다면 여러분의 애플리케이션에서 더 빠른 LibXML이나 Nokogiri 구현을 쉽게 지정할 수 있습니다:
XmlMini.backend = 'LibXML'
- 주요 기여자: Bart ten Brinke
- 주요 기여자: Aaron Patterson
7.4 TimeWithZone의 소수점 초
Time
과 TimeWithZone
클래스는 XML 친화적인 문자열로 시간을 반환하는 xmlschema
메서드를 포함합니다. Rails 2.3부터 TimeWithZone
은 Time
과 동일하게 반환되는 문자열의 소수점 초 부분의 자릿수를 지정하는 인수를 지원합니다:
Time.zone.now.xmlschema(6) # => "2009-01-16T13:00:06.13653Z"
- 리드 컨트리뷰터: Nicholas Dainty
7.5 JSON Key Quoting
"json.org" 사이트의 명세를 살펴보면, JSON 구조의 모든 key는 반드시 문자열이어야 하고 큰따옴표로 감싸져야 한다는 것을 알 수 있습니다. Rails 2.3부터는 숫자로 된 key의 경우에도 이 규칙을 올바르게 적용하고 있습니다.
7.6 기타 Active Support 변경사항
Enumerable#none?
을 사용하여 어떤 요소도 제공된 block과 일치하지 않는지 확인할 수 있습니다.- Active Support delegates를 사용하는 경우, 새로운
:allow_nil
옵션을 통해 target 객체가 nil일 때 예외를 발생시키는 대신nil
을 반환할 수 있습니다. ActiveSupport::OrderedHash
: 이제each_key
와each_value
를 구현합니다.ActiveSupport::MessageEncryptor
는 신뢰할 수 없는 위치(예: cookies)에 저장하기 위한 정보를 암호화하는 간단한 방법을 제공합니다.- Active Support의
from_xml
은 더 이상 XmlSimple에 의존하지 않습니다. 대신 Rails는 이제 필요한 기능만을 가진 자체 XmlMini 구현을 포함합니다. 이를 통해 Rails는 지금까지 가지고 다녔던 XmlSimple의 번들 복사본을 제거할 수 있게 되었습니다. - private 메서드를 memoize하면, 그 결과도 private이 됩니다.
String#parameterize
는 선택적 구분자를 받습니다:"Quick Brown Fox".parameterize('_') => "quick_brown_fox"
.number_to_phone
이 이제 7자리 전화번호를 지원합니다.ActiveSupport::Json.decode
가 이제\u0000
스타일의 이스케이프 시퀀스를 처리합니다.
8 Railties
위에서 다룬 Rack 변경사항 외에도, Railties(Rails 자체의 핵심 코드)는 Rails Metal, 애플리케이션 템플릿, quiet backtraces를 포함한 여러 중요한 변경사항을 포함합니다.
8.1 Rails Metal
Rails Metal은 Rails 어플리케이션 내에서 초고속 엔드포인트를 제공하는 새로운 메커니즘입니다. Metal 클래스는 라우팅과 Action Controller를 우회하여 순수한 속도를 제공합니다(물론 Action Controller의 모든 기능을 포기하는 대가로). 이는 Rails를 미들웨어 스택이 노출된 Rack 어플리케이션으로 만들기 위한 최근의 모든 기초 작업을 기반으로 합니다. Metal 엔드포인트는 어플리케이션이나 플러그인에서 로드할 수 있습니다.
- 추가 정보:
8.2 Application Templates
Rails 2.3은 Jeremy McAnally의 rg application generator를 통합했습니다. 이는 이제 Rails에 template 기반의 애플리케이션 생성이 내장되어 있다는 것을 의미합니다. 모든 애플리케이션에 포함하는 plugin 세트가 있는 경우(다른 많은 사용 사례들 중에서), template을 한 번 설정하고 rails
명령을 실행할 때마다 반복해서 사용할 수 있습니다. 기존 애플리케이션에 template을 적용하는 rake task도 있습니다:
$ rake rails:template LOCATION=~/template.rb
이는 프로젝트에 이미 포함된 코드 위에 template의 변경사항을 덧붙일 것입니다.
- Lead Contributor: Jeremy McAnally
- More Info: Rails templates
8.3 Quieter Backtraces
thoughtbot의 Quiet Backtrace 플러그인을 기반으로 하여, Test::Unit
backtrace에서 선택적으로 라인을 제거할 수 있게 되었으며, Rails 2.3은 ActiveSupport::BacktraceCleaner
와 Rails::BacktraceCleaner
를 코어에 구현했습니다. 이는 backtrace 라인에서 정규식 기반 치환을 수행하는 filter와 backtrace 라인을 완전히 제거하는 silencer를 모두 지원합니다. Rails는 새로운 애플리케이션에서 가장 일반적인 노이즈를 제거하기 위해 자동으로 silencer를 추가하고, 사용자가 직접 추가할 수 있도록 config/backtrace_silencers.rb
파일을 생성합니다. 이 기능은 또한 backtrace에 있는 모든 gem의 더 예쁜 출력을 가능하게 합니다.
8.4 개발 모드에서 Lazy Loading/Autoload를 통한 더 빠른 부팅 시간
Rails(및 그 의존성)의 각 부분이 실제로 필요할 때만 메모리에 로드되도록 많은 작업이 이루어졌습니다. 핵심 프레임워크인 Active Support, Active Record, Action Controller, Action Mailer, Action View는 이제 autoload
를 사용하여 개별 클래스를 지연 로드합니다. 이 작업은 메모리 사용량을 줄이고 전반적인 Rails 성능을 향상시키는 데 도움이 될 것입니다.
또한 핵심 라이브러리를 시작 시점에 자동으로 로드해야 하는지 여부를 (새로운 preload_frameworks
옵션을 사용하여) 지정할 수 있습니다. 이는 기본적으로 false
로 설정되어 있어 Rails가 부분적으로 자동 로드되지만, 모든 것을 한 번에 로드해야 하는 경우도 있습니다 - Passenger와 JRuby는 모든 Rails가 함께 로드되는 것을 필요로 합니다.
8.5 rake gem Task 리팩토링
다양한 rake gem
태스크의 내부 동작이 여러 상황에 더 효과적으로 작동하도록 대폭 수정되었습니다. gem 시스템은 이제 development와 runtime 의존성의 차이를 인식하고, 더 안정적인 압축 해제 시스템을 갖추고 있으며, gem의 상태를 조회할 때 더 나은 정보를 제공하고, 초기 설정 시 "닭과 달걀" 의존성 문제에 덜 취약합니다. 또한 JRuby에서 gem 명령어를 사용하는 것과 이미 vendor에 포함된 gem들의 외부 복사본을 가져오려는 의존성 문제도 수정되었습니다.
- 주요 기여자: David Dollar
8.6 기타 Railties 변경사항
- Rails를 빌드하기 위한 CI 서버 업데이트 지침이 업데이트되고 확장되었습니다.
- Rails 내부 테스팅이
Test::Unit::TestCase
에서ActiveSupport::TestCase
로 전환되었으며, Rails 코어는 테스트를 위해 Mocha가 필요합니다. - 기본
environment.rb
파일이 정리되었습니다. - dbconsole 스크립트가 이제 숫자로만 된 비밀번호를 충돌 없이 사용할 수 있습니다.
Rails.root
가 이제Pathname
객체를 반환하므로,File.join
을 사용하는 기존 코드를 정리하기 위해join
메서드를 직접 사용할 수 있습니다.- CGI와 FCGI 디스패칭을 다루는 /public의 다양한 파일들이 더 이상 모든 Rails 애플리케이션에서 기본적으로 생성되지 않습니다(
rails
명령어 실행 시--with-dispatchers
를 추가하거나 나중에rake rails:update:generate_dispatchers
로 추가할 수 있습니다). - Rails 가이드가 AsciiDoc에서 Textile 마크업으로 변환되었습니다.
- Scaffold된 뷰와 컨트롤러가 약간 정리되었습니다.
script/server
가 이제 특정 경로에서 Rails 애플리케이션을 마운트하기 위한--path
인자를 받습니다.- 설정된 gem이 누락된 경우, gem rake 작업이 환경의 많은 부분을 로드하지 않고 건너뜁니다. 이는 gem이 누락되어 rake gems:install을 실행할 수 없었던 많은 "닭과 달걀" 문제를 해결할 것입니다.
- Gem들은 이제 정확히 한 번만 압축 해제됩니다. 이는 파일들에 읽기 전용 권한으로 패킹된 gem(예: hoe)의 문제를 해결합니다.
9 Deprecated
이번 릴리스에서 몇 가지 오래된 코드가 deprecated 되었습니다:
- inspector, reaper, spawner 스크립트에 의존하는 배포 방식을 사용하는 (비교적 드문) Rails 개발자라면, 이 스크립트들이 더 이상 core Rails에 포함되지 않는다는 점을 알아야 합니다. 필요한 경우 irs_process_scripts 플러그인을 통해 구할 수 있습니다.
- Rails 2.3에서
render_component
는 "deprecated"에서 "nonexistent"가 되었습니다. 여전히 필요하다면 render_component 플러그인을 설치할 수 있습니다. - Rails 컴포넌트에 대한 지원이 제거되었습니다.
script/performance/request
를 실행하여 통합 테스트 기반의 성능을 확인하는 데 익숙했던 사람들은 새로운 방법을 배워야 합니다: 이 스크립트가 이제 core Rails에서 제거되었습니다. 동일한 기능을 되살리기 위한 새로운 request_profiler 플러그인을 설치할 수 있습니다.- 세션이 이제 lazy-load되므로
ActionController::Base#session_enabled?
는 deprecated 되었습니다. protect_from_forgery
의:digest
와:secret
옵션이 deprecated되었으며 아무 효과가 없습니다.- 일부 통합 테스트 헬퍼가 제거되었습니다.
response.headers["Status"]
와headers["Status"]
는 더 이상 아무것도 반환하지 않습니다. Rack은 반환 헤더에 "Status"를 허용하지 않습니다. 하지만 여전히status
와status_message
헬퍼를 사용할 수 있습니다.response.headers["cookie"]
와headers["cookie"]
는 더 이상 CGI 쿠키를 반환하지 않습니다.headers["Set-Cookie"]
를 검사하여 원시 쿠키 헤더를 보거나cookies
헬퍼를 사용하여 클라이언트로 전송된 쿠키의 해시를 얻을 수 있습니다. formatted_polymorphic_url
가 deprecated 되었습니다. 대신:format
과 함께polymorphic_url
를 사용하세요.ActionController::Response#set_cookie
의:http_only
옵션이:httponly
로 이름이 변경되었습니다.to_sentence
의:connector
와:skip_last_comma
옵션이:words_connector
,:two_words_connector
,:last_word_connector
옵션으로 대체되었습니다.- 빈
file_field
컨트롤이 있는 multipart 폼을 제출하면 이전에는 컨트롤러에 빈 문자열을 제출했습니다. 이제는 Rack의 multipart 파서와 이전 Rails 파서의 차이로 인해 nil을 제출합니다.
10 크레딧
릴리스 노트는 Mike Gunderloy가 작성했습니다. 이 버전의 Rails 2.3 릴리스 노트는 Rails 2.3 RC2를 기반으로 작성되었습니다.