04. 액션팩
액션팩
액션뷰 (HTML, XML, JavaScript 등과 같이 최종 사용자에게 보여질 비주얼을 생성) 와 액션컨트롤러 (비즈니스 플로우 컨트롤)로 구성된다. (출처: 위키피디아)
타임존
타임존 기본값 정의하기
새로추가된 옵션 time_zone_select 메소드로 사용자가 어떠한 타임존도 선택하지 않은 경우 또는 데이타베이스 컬럼이 null 일 때에도 기본값으로 제공할 수 있다. 이렇게 하기 위해서 :default 옵션을 써서 다음과 같이 사용할 수 있다.
- time_zone_select("user", "time_zone", nil, :include_blank => true)
time_zone_select("user", "time_zone", nil,
:default => "Pacific Time (US & Canada)" )
time_zone_select( "user", 'time_zone', TimeZone.us_zones,
:default => "Pacific Time (US & Canada)")
:default 옵션을 사용한 예에서 보이는 것 처럼 TimeZone이 이미 선택되어진 것을 볼 수 있다.
formatted_offset 메소드
formatted_offset 메소드는 UTC 시간의 편차값으로 된 +HH:MM 포멧을 리턴하기 위한 Time 과 DateTime 클래스를 포함하고 있다. 예를 들면 우리 타임존(브라질리아 타임)에서 이 메소드에 의해서 리턴되는 편차값은 "-03:00" 의 스트링 값이 될 것이다.
예를 보자:
DateTime 으로 부터 얻어지는 편차값.
- datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
datetime.formatted_offset # => "-06:00″
datetime.formatted_offset(false) # => "-0600″
Time 으로 부터 얻어지는 값.
- Time.local(2000).formatted_offset # => "-06:00″
Time.local(2000).formatted_offset(false) # => "-0600″
이 메소드는 포멧된 형태이거나 파라미터로 주어진 값에 의존하지 않는 스트링을 리턴한다는 것을 주의하라.
with_env_tz 메소드
with_env_tz 메소드는 아주 간단한 형태로 서로다른 타임존을 테스트할 수 있게 해준다.
- def test_local_offset
with_env_tz 'US/Eastern' do
assert_equal Rational(-5, 24), DateTime.local_offset
end
with_env_tz 'US/Central' do
assert_equal Rational(-6, 24), DateTime.local_offset
end
end
이 헬퍼 메소드는 with_timezone 을 호출하기 위한 거라 생각하지만 ENV['TZ']를 사용해서 얻어지는 타임존과 Time.zone 의 혼란을 피하기 위해서 with_env_tz 로 리네임 되었다.
Time.zone_reset!
삭제되어서 더이상 사용되지 않음.
Time#in_current_time_zone
Time.zone 이 null 인 경우 self 를 리턴하도록 수정됨.
Time#change_time_zone_to_current
Time.zone 이 null 인 경우 self 를 리턴하도록 수정됨.
TimeZone#now
TimeZone#now 메소드는 Time.zone 에 정의된 현재 타임존 설정을 나타내는 ActiveSupport::TimeWithZone 을 리턴하도록 수정됨. 예를 들면
- Time.zone = 'Hawaii' # => "Hawaii"
Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00
Compare_with_coercion
compare_with_coercion (일명 : <=>) 메소드는 Time과 DateTime 클래스에서 생성되었다. 이 클래스는 Time, DateTime 클래스와 ActiveSupport::TimeWithZone 오브젝트 인스턴스 간 연대순 비교로 만들 수 있게된다. 좀더 쉬운 설명을 위해 아래 예를 보자 (각 라인의 결과는 코드 끝에 위치한 커맨트로 나와있다)
- Time.utc(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59, 999) # 1
Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0) # 0
Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0, 001)) # -1
Time.utc(2000) <=> DateTime.civil(1999, 12, 31, 23, 59, 59) # 1
Time.utc(2000) <=> DateTime.civil(2000, 1, 1, 0, 0, 0) # 0
Time.utc(2000) <=> DateTime.civil(2000, 1, 1, 0, 0, 1)) # -1
Time.utc(2000) <=> ActiveSupport::TimeWithZone.new(Time.utc(1999, 12, 31, 23, 59, 59) )
Time.utc(2000) <=> ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0) )
Time.utc(2000) <=> ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 1) ))
TimeWithZone#between?
between? 메소드는 인스턴스가 두 날짜사이에 존재하는지 검증하기 위한 TimeWithZone 클래스에 포함되어 있다.
예.
- @twz.between?(Time.utc(1999,12,31,23,59,59),
Time.utc(2000,1,1,0,0,1))
TimeZone#parse
이 메소드는 스트링으로부터 ActiveSupport::TimeWIthZone 의 새 인스턴스를 생성한다.
예를 들면.
- Time.zone = "Hawaii"
# => "Hawaii"
Time.zone.parse('1999-12-31 14:00:00')
# => Fri, 31 Dec 1999 14:00:00 HST -10:00
Time.zone.now
# => Fri, 31 Dec 1999 14:00:00 HST -10:00
Time.zone.parse('22:30:00')
# => Fri, 31 Dec 1999 22:30:00 HST -10:00
TimeZone#at
이 메소드는 Unix 에서 사용되는 초의 값으로 부터 ActiveSupport::TimeWIthZone 의 인스턴스를 생성하는데 사용될 수 있다.
예를 들면.
- Time.zone = "Hawaii" # => "Hawaii"
Time.utc(2000).to_f # => 946684800.0
Time.zone.at(946684800.0)
# => Fri, 31 Dec 1999 14:00:00 HST -10:00
더 많은 메소드들
to_a, to_f, to_i, httpdate, rfc2822, to_yaml, to_datetime, eql? 메소드는 TimeWithZone 클래스에 추가되었다. 이 메소드에 대한 추가 정보는 Rails 문서를 참고하라.
루비 1.9를 위해 준비하는 TimeWithZone 클래스
루비 1.9 에는 Time 클래스에는 다음과 같은 새로운 메소드들이 추가된다.
- Time.now
# => Thu Nov 03 18:58:25 CET 2005
Time.now.sunday?
# => false
요일을 나타내는 각각의 메소드가 존재한다.
흥미로운 것은 Time 오브젝트의 to_s 메소드가 다른 리턴 값을 가진다는 것이다. Time.new.to_s 가 실행될때 Today 는 다음과 같다.
- Time.new.to_s
# => "Thu Oct 12 10:39:27 +0200 2006″
루비 1.9 에서는
- Time.new.to_s
# => "2006-10-12 10:39:24 +0200″
레일스 2.1 에서 해야만 하는 것은 무었인가? 모든것이다. 레일스는 이런 수정사항들을 다룰 준비가 되어있다. 예를 들면 TimeWithZone 클래스는 첫번째 예제의 메소드와 같이 동작하기 위해서 개선사항을 수용했다.
오토링크
auto_link 메소드는 파라미터로 주어진 어떠한 텍스트라도 수용한다. 만약 텍스트가 이메일이나 웹사이트 주소라도 동일한 하이퍼링크로된 텍스트를 리턴한다.
예를 들면
- auto_link("Go to this website now: http://www.rubyonrails.com")
# => Go to this website now: http://www.rubyonrails.com
아마존 같은 사이트는 "=" 심볼을 URL의 일부로 사용한다. 이 메소드는 이러한 심볼을 인식하지 않는다. 이런 경우 메소드가 어떻게 동작하는지 보라.
- auto_link("http://www.amazon.com/Testing/ref=pd_bbs_sr_1")
# => http://www.amazon.com/Testing/ref
메소드가 하이퍼링크를 정확하게 "=" 심볼 앞에서 끝난 것에 주의하라, 레일스 2.1 이전에는 이런 심볼은 지원되지 않았다.
같은 메소드가 나중에 URL에 괄호가 있는 것을 사용할 수 있도록 업데이트 되었다.
괄호가 있는 URL 예
- http://en.wikipedia.org/wiki/Sprite_(computer_graphics)
레이블
스캐폴드를 사용해서 새로운 폼을 생성하면 다음과 같은 코드가 생성될 것이다.
- <% form_for(@post) do |f| %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.label :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.submit "Update" %>
</p>
<% end %>
lable 메소드가 포함되었다. 이 메소드는 HTML 태그에 컬럼 타이틀을 가진 스트링을 리턴한다.
- >> f.label :title
=> <label for="post_title">Title</label>
>> f.label :title, "A short title"
=> <label for="post_title">A short title</label>
>> label :title, "A short title", :class => "title_label"
=> <label for="post_title" class="title_label">A short title</label>
태그에 있는 for 파라미터가 보이는가? "post_title" 은 포스트 타이틀을 담고 있는 텍스트 박스 타이틀이다. \ 태그는 사실 post_title 오브젝트와 연결된 레이블이다. 누군가 이 레이블 (링크가아님)을 클릭할 때 연결된 HTML 컨트롤러가 포커스를 받게된다.
Robby Russell 은 이 주제에 관한 흥미로은 포스팅을 그의 블로그에 썼다. 여기서 읽을 수 있다. http://www.robbyonrails.com/articles/2007/12/02/that-checkbox-needs-a-label
또한 FormTagHelper 에 lable_tag 메소드가 포함되어있다. 이 메소드는 레이블 처럼 하지만 좀 더 심플한 방식으로 동작한다.
- >> label_tag 'name'
=> <label for="name">Name</label>
>> label_tag 'name', 'Your name'
=> <label for="name">Your name</label>
>> label_tag 'name', nil, :class => 'small_label'
=> <label for="name" class="small_label">Name</label>
이 메소드는 또한 :for 옵션을 수용한다. 예를 보자
- label(:post, :title, nil, :for => "my_for")
이것은 다음과 같이 리턴될 것이다.
- <label for="my_for">Title</label>
파셜의 새로운 사용법
레일스로 소프트웨어 개발시에 파셜을 사용해서 코드의 중복을 피하는 것은 매우 일반적이다. 여기 예제가 있다.
- <% form_for :user, :url => users_path do %>
<%= render :partial => 'form' %>
<%= submit_tag 'Create' %>
<% end %>
파셜은 코드 조각 (템플릿) 이다. 파셜 사용의 장점은 불필요한 코드 중복을 피하는 것이다. 파셜의 사용은 매우 간단하다. render partial => "name" 과 같이 시작할 수 있다. 같은 이름으로된 파셜 파일을 생성해야만 하며 앞에 언더라인을 붙여야 한다.
위에 보이는 코드는 어떻게 하는지 보여준다. 레일스 2.1 에서는 약간 다른 방법으로 같은 것을 하게 된다.
- <% form_for(@user) do |f| %>
<%= render :partial => f %>
<%= submit_tag 'Create' %>
<% end %>
이 예에서는 FormBuilder 에 의해서 생성된 "form" 이라는 레퍼런스 변수를 받아 "users/_form" 파셜을 랜더링 한다
이전 방식도 레일스 2.1 에서 여전히 동작한다.
Atom 피드의 새 네임스페이스
atom_feed 메소드를 아는가? 이것은 Atom 피드를 쉽게 만들게 해주는 레일스 2.0 의 새 기능중 하나이다.
사용 예를 보자.
index.atom.builder 파일에서.
- atom_feed do |feed|
feed.title("Nome do Jogo")
feed.updated((@posts.first.created_at))
for post in @posts
feed.entry(post) do |entry|
entry.title(post.title)
entry.content(post.body, :type => 'html')
entry.author do |author|
author.name("Carlos Brando")
end
end
end
end
Atom 피드가 무었인가? Atom 은 XML 기반 스타일과 메타 데이타의 이름이다. 다시 말하면 인터넷에서 예를 들어 블로그처럼 자주 업데이트되는 컨텐츠를 출판하기 위한 프로토콜이다. 피드는 항상 XML로 출판되고 Atom 은 application/atom+xml 메타 타입으로 구분된다.
레일스 2.0 초기 버젼에서 이 메소드는 :language, :root_url, :url 옵션이 파라미터로 사용되었다. 이 메소드에 대한 추가적인 정보는 레일스 문서에서 얻을 수 있다. 업데이트 된 내용은 피드의 루트 엘리먼트에 새로운 네임스페이스가 포함될 수 있다는 것이다. 예를 들면.
- atom_feed('xmlns:app' => 'http://www.w3.org/2007/app') do |feed|
다음을 리턴한다.
- <feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom"
xmlns:app="http://www.w3.org/2007/app">
이전에 사용했던 예를 수정하면 이런 식으로 사용할 수 있다.
- atom_feed({'xmlns:app' => 'http://www.w3.org/2007/app',
'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
feed.title("Nome do Jogo")
feed.updated((@posts.first.created_at))
feed.tag!(openSearch:totalResults, 10)
for post in @posts
feed.entry(post) do |entry|
entry.title(post.title)
entry.content(post.body, :type => 'html')
entry.tag!('app:edited', Time.now)
entry.author do |author|
author.name("Carlos Brando")
end
end
end
end
캐시
모든 fragment_cache_key 메소드는 'view/' 접두사를 네임스페이스의 기본값으로 리턴한다.
모든 캐시 스토어는 ActionController::Caching::Fragments::* 에서 제거되었고 이제 ActiveSupport::Cache::* 에서 찾을 수 있다. 예를 들어 만일 ActionController::Caching::Fragments::MemoryStore 와 같은 저장소 레퍼런스를 만드는 경우 이 레퍼런스는 ActiveSupport::Cache::MemoryStore 와 같이 변경해야만 할 것이다.
ActionController::Base.fragment_cache_store 는 ActionController::Base.cache_store 가 그 역할을 대체한다. ActiveRecord::Base 에 포함된 cache_key 메소드는 신규 라이브러리 ActionSupport::Cache::*로 액티브 레코드 캐시 저장을 쉽게해준다. 다음과 같이 동작한다.
- >> Product.new.cache_key
=> "products/new"
>> Product.find(5).cache_key
=> "products/5"
>> Person.find(5).cache_key
=> "people/5-20071224150000"
ActiveSupport::Gzip.decompress/compress 가 포함되어 Zlib 의 래퍼로서 사용하기 쉽도록 해준다.
environment 옵션으로 config.cache_store 를 사용해서 기본 캐시 저장소를 지정할 수 있다. 만약 tmp/cache 디렉토리가 존재한다면 기본 저장소는 FileStore 가 되고 그렇지 않다면 MemoryStore 가 사용될 것이다. 다음과 같이 설정할 수 있다.
- config.cache_store = :memory_store
config.cache_store = :file_store, "/path/to/cache/directory"
config.cache_store = :drb_store, "druby://localhost:9192"
config.cache_store = :mem_cache_store, "localhost"
config.cache_store = MyOwnStore.new("parameter")
좀더 쉽게 하기 위해서 environments/production.rb 파일 커맨트 밑에 리마인드 차원에서 이 옵션이 포함되어있다.
- # Use a different cache store in production
# config.cache_store = :mem_cache_store
스트링에 타이틀 포멧 적용하기
's 를 포함하고 있는 스트링에 String#titleize 메소드를 사용하는 경우 버그가 있다. 이 버그는 's 를 대문자로 만들어 버린다. 예를 보자.
- >> "brando’s blog".titleize
=> "Brando’S Blog"
버그가 수정되었다.
- >> "brando’s blog".titleize
=> "Brando’s Blog"
action_name
뷰 런타임시에 어떤 엑션이 호출했는지 찾아내기 위해서 action_name 메소드를 사용할 수 있다.
- <%= action_name %>
리턴값은 params[:action] 을 사용하는 것과 같지만 좀더 우아한 방법이다.
caches_action accepts conditionals
caches_action 메소드는 이제 action 이 캐시될 경우를 명시하기 위한 조건으로 사용하도록 하는 :if 옵션을 수용한다. 예를 보자.
- caches_action :index, :if => Proc.new { |c| !c.request.format.json? }
위의 예에서 action index 는 JSON 요청이 아닌 경우에만 캐시될 것 이다.
Conditional in the caches_page method
caches_page 메소드는 이제 (:if) 조건 옵션을 갖는다. 예를 보자.
- # The Rails 2.0 way
caches_page :index
# In Rails 2.1 you can use :if option
caches_page :index, :if => Proc.new { |c| !c.request.format.json? }
테스트에서 동작하게된 flash.now
누가 이것 때문에 머리 아프지 않았을까? 문제는 테스트하는 동안 flash 에 저장된 메시지가 있더라도 결코 확인할 수 없었다는 것이다. 왜냐하면 테스트 스크립트가 수행되기 전에 레일스에 의해서 제거 되었기 때문이다.
레일스 2.1 에서는 문제가 해결되었다. 이제 테스트에서 아래처럼 할 수 있다.
- assert_equal '>value_now<', flash['test_now']
뷰 외부에서 헬퍼 접근하기
얼마나 자주 helper 를 작성하고 컨트롤러 내부에서 사용하려고 했을까? 이런 기능성을 달성하기 위해서 코드가 좀 지저분해지지만 helper 모듈을 컨트롤러 내부에 포함시켜야만 한다.
레일스 2.1 에서는 뷰 외부에서 헬퍼에 접근하는 프락시가 적용되었다. 매우 간단한 방법으로 작동한다.
- # To access simple_format method, for example
ApplicationController.helpers.simple_format(text)
간단하고 명료하다!
JSON
레일스는 이제 JSON 컨텐츠의 POST 요청을 수용한다. 예를 들면 이제 POST 를 다음과 같이 보낼 수 있다.
- POST /posts
{"post": {"title": "Breaking News"}}
그리고 모든 것은 params 변수로 전달된다. 예제와 같이 동작한다.
- def create
@post = Post.create params[:post]
# …
end
일부 개발자들은 JSON 이 XML 의 "경쟁자" 이고 언어 자체로 표현되는 것 때문에 자바스크립트 데이타 교환에 광범위하게 사용된다는 것을 모를 수 있다. 그 이름은 JavaScript Object Notation 에서 따왔다.
Path Names
내 블로그(http://www.nomedojogo.com) 독자라면 Custom Resource Name 플러그인에 대해서 알고 있을 것이다. 이건 곧 사라질 거라 생각한다 ... :(
이미 라우트 (내 플러그인에서 호환성을 유지하려고 구현한 것들)에서 :as 옵션을 포함하고 있을 것이다. 이제 엑션의 이름을 바꾸기 위해서 :path_name 옵션 또한 사용할 수 있다.
- map.resource :schools, :as => 'escolas', :path_names => { :new => 'nova' }
물론 내 플러그인은 이전 레일스 버젼 사용자에게 여전히 유용할 것 이다.
라우트 파일 위치 정의하기
레일스 2.1 에서는 environment.rb 파일에 다음과 같은 내용을 포함해서 어떤 파일에 라우트 정보가 저장될지 지정할 수 있다.
- config.routes_configuration_file
이것은 두개의 분리된 front-end 를 가지고 있고 같은 모듈, 라이브러리, 플러그인을 공유하고 있는 시나리오에서 유용할 수 있다.
예를 들면, getsatisfaction.com 과 api.getsatisfaction.com 이 같은 모듈을 공유하지만 컨트롤러나 헬퍼 뷰는 공유하지 않는다. getsatisfaction 은 API 라우트 파일이 SEO 향상에 대해서 아무 것도 모르더라도 SEO 를 향상하기 위해서 최적화된 별도의 라우트 파일을 가진다.
session(:on)
레일스에서 세션을 꺼버릴 수 있다는 것을 알고 있는가? 어떻게 하는지 보자.
- class ApplicationController < ActionController::Base
session :off
end
예에서 나는 모든 컨트롤러(ApplicationController)의 세션을 꺼버렸다는 것을 주의하라. 하지만 한 컨트롤러에서만 다시 사용할 수도 있다.
만약 레일스 2.1 에서 주어진 컨트롤러에서 세션을 사용하길 원한다면 session 메소드를 사용해서 :on 파라미터를 전달하면 된다.
- class UsersController < ApplicationController
session :on
end
간단하게 헬퍼 테스트하기
레일스 초기 버젼에서 매우 지루한 것중에 하나는 헬퍼를 테스트 하는 것이다. 나는 이미 헬퍼의 테스트를 생성해서 100% 커버리지를 달성하기 위해서 엄청난 압박의 고통을 겪었다.
이것은 레일스 2.1 에서 ActionView::TestCase 로 인해 더욱 간단해 졌다. 예제를 보자.
- module PeopleHelper
def title(text)
content_tag(:h1, text)
end
def homepage_path
people_path
end
end
이제 레일스 2.1 에서 어떻게 똑같이 할 수있는지 보자.
- class PeopleHelperTest < ActionView::TestCase
def setup
ActionController::Routing::Routes.draw do |map|
map.people 'people', :controller => 'people', :action => 'index'
map.connect ':controller/:action/:id'
end
end
def test_title
assert_equal "<h1>Ruby on Rails</h1>", title("Ruby on Rails")
end
def test_homepage_path
assert_equal "/people", homepage_path
end
end
History
Last edited on 06/24/2008 18:15 by JasonPA
Comments (0)