第5章: ActionPack
By:Caiwangqin
包含 ActionView (为最终用户生成显示,象 HTML,XML,JavaScript) 和 ActionController (业务流程控制)。
TIMEZONE
定义一个默认的时区
一个新的选项被加入到 time_zone_select 方法,在你的用户没有选择任何 TimeZone 的时候,或当数据库字段为空时,你现在可以显示一个默认值。它已经创建了一个 :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 方法被包含在 Time 和 DateTime 类中,返回+HH:MM格式的UTC时差。例如,在我们的时区(北京时间),这个方法返回的时差是一个字符串"+08: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_tzwith_env_tz 方法 允许我们以非常简单的方式测试不同的时区:
def test_local_offset
with_env_tz 'US/Eastern' do
ssert_equal Rational(-5, 24), DateTime.local_offset
end
with_env_tz 'US/Central' do
assert_equal Rational(-6, 24), DateTime.local_offset
end
end
这个 Helper 可以调用 with_timezone, 但为了避免使用 ENV["TZ"] 和 Time.zone 时混乱,它被重命名为 with_env_tz.
Time.zone_reset!该方法已经被删除
Time#in_current_time_zone该方法修改为当 Time.zone 为空时返回 self
Time#change_time_zone_to_current该方法修改为当 Time.zone 为空时返回 self
TimeZone#now该方法修改为返回 ActiveSupport::TimeWithZone 显示当前在 Time.zone 中已设定的时区。例如:
Time.zone = 'Hawaii' # => "Hawaii"
Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00
Compare_with_coercion在 Time 和 DateTime 类中新增加了一个方法 compare_with_coercion(和一个别名 <=>), 它使在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#betwwen?在 TimeWithZone 类中包含 between? 方法检验一个实例被创建在两个日期之间。
@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 epoch 数字创建一个 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 文档。
TimeWithZone 为 Ruby 1.9 准备在 Ruby 1.9中,我们有了一些新的方法在 Time 类中,如:
Time.now
# => Thu Nov 03 18:
Time.now.sunday?
# => false
另一个新的是 Time 的 to_s 方法将会有一个不同的返回值。现在当我们执行 Time.new.to_s, 将得到:
Time.new.to_s
# => "Thu Oct 12 10:39:27 +0200 2006″
在 Ruby 1.9 中我们将得到:
Time.new.to_s
# => "2006-10-12 10:39:24 +0200″
Rails 2.1拥有哪些相关的东西? 答案是所有东西,从 Rails 开始准备这些修改。TimeWithZone 类,例如,刚收到一个为第一个示例工作的方法实现。
AUTO LINK为那些不知道这个方法的人,auto_link 方法接收所有文本参数,如果这个文本包含一个e-mail地址或一个网址,它将返回相同的文本,但有了超链接:
例如:
auto_link("Go to this website now: http://www.chinaonrails.com")
# => Go to this website now: http://www.chinaonrails.com
一些网站,象 Amazon, 使用 "=" 号在URL中,该方法不认可这个字符,看这个方法怎样处理这种情况:
auto_link("http://www.amazon.com/Testing/ref=pd_bbs_sr_1")
# => http://www.amazon.com/Testing/ref
注意该方法截断链接地址在 "=" 号前,因为它不支持这个符号。我产意思是,它通常是不被支持的,在Rails 2.1中,我们解决了这个问题。
同样的方法将在稍后更新,允许在URL's中使用括号。
一个使用括号的示例:
http://en.wikipedia.org/wiki/Sprite_(computer_graphics)
LABELS当使用 scaffold 生成一个新表单时,它将创建下面的代码:
<% 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 %>
这种方式更有意义,它包含了 Label 方法。该方法在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 tile 的文本框。这个<label>标签实际是一个关联到 post_title 对象。当有人点击这个 label(非链接) 时,被关联的控件接收到焦点。
Robby Russell 在他的Blog中写了一个有趣的关于这个主题的文章。你可以从这里阅读:http://www.robbyonrails.com/articles/2007/12/02/that-checkbox-needs-a-label
在 FormTagHelper 中同样也拥有 label_tag方法。该方法的工作方式和label一样,但更简单:
>> 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>
一个新的使用PARTIALS方式在Rails开发过程中使用 partials 避免重复代码是很觉的方式。例如:
<% form_for :user, :url => users_path do %>
<%= render :partial => 'form' %>
<%= submit_tag 'Create' %>
<% end %>
Partial 是一个代码片断(模板)。使用partial的是避免不需要的代码重复。使用partial非常简单,你可以这样开始:render :partial => "name". 之后,你必须创建一个与你的partial同名的文件,但使用一个下划线在这之前。
上面的代码是我们通常的方式,但在新的Rails版本中,我们使用不同的方式做相同的事,如:
<% form_for(@user) do |f| %>
<%= render :partial => f %>
<%= submit_tag 'Create' %>
<% end %>
在这个示例中我们使用了partias "users/_form", 将接收到一个名为"form"的被 FormBuilder 创建的变量。
以前的方式将继续工作。
新的NAMESPACE在ATOM FEED你知道atom_feed方法吗?这是Rails 2.0的一个新特性,使创建Atom feeds变得更容易。看一个使用方法:
在一个 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 feed? Atom 的名字是基于 XML 样式的 meta 数据。在互联网中它是一个发布经常更新的内容的协议,如Blog. 例如,Feeds 经常以XML或Atom的格式发布标示为application/atom+xml 类型.
在Rails 2.0的第一个版本中,该方法允许 :language, :root_url 和 :url 参数,你可以从Rails文档中获得更多关于这些方法的信息。但基于这一个更新,我们可以包含新的命名空间在一个Feed的root元素中,例如:
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
CACHE现在所有的 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 取代了它的位置。
由于这个新的 ActiveSupport::Cache::* 库,它使在 ActiveRecord::Base 中的 cache_key 方法容易缓存一个 Active Records ,它这样工作:
>> 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 方法时有一个 bug. 这个 bug 返回大写的 'S, 看一个示例:
>> "brando’s blog".titleize
=> "Brando’S Blog"
看相当的示例,但已经修复了这个bug:
>> "brando’s blog".titleize
=> "Brando’s Blog"
ACTION_NAME现在,要知道在运行时哪一个 view 被调用, 我们只需要使用 action_name 方法:
<%= action_name %>
返回值将和使用 params[:action]一样,但更优雅。
CACHES_ACTION 支持条件caches_action 方法现在支持 :if 选项,允许使用条件指定一个 cache 可以被缓存。例如:
caches_action :index, :if => Proc.new { |c| !c.request.format.json? }
在上面的例子中,只有当请求不是JSON的时候 action index 将被缓存。
在CACHES_PAGE方法中的条件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 现在可以在 TESTS 中工作谁没有这因为这而头痛过?这个问题在我们测试期间,无法确定信息已经存储到了Flash中,因为它在到你的测试代码之前就被Rails清除了。
在 rails 2.1中这个问题已经被解决。现在你可以包含下面的代码行在你的测试中:
assert_equal '>value_now<', flash['test_now']
在VIEWS之外访问HELPERS有多少次你创建了一个 helper 希望希望它在一个 controller 中使用?要做到这样,你需要包含这个 helper module 到这个 controller中,但这使你的代码看起来不干净。
Rails 2.1已经开发了一个方法在VIEWS之外的HELPERS. 它以很简单的方式工作:
# To access simple_format method, for example
ApplicationController.helpers.simple_format(text)
简单而干净。
JSON
Rails 现在允许 POST's 一个 JSON 内容的请求,例如,你可以象这样发送一个 POST:
POST /posts
{"post": {"title": "Breaking News"}}
所有参数都将到 params中。例如:
def create
@post = Post.create params[:post]
# …
end
为了那些不知道JSON是一个XML竞争者的人,它在JavaScript数据交换中使用相当广泛,因为它呈现为这种语言。它的名字来源于:JavaScript Object Notation.
PATH NAME我的Blog(http://www.nomedojogo.com) 读者们应该知道我的 Custom Resource Name 插件,这想它很快就要死亡了…:(
在rails中你已经包含了 :as 选项在routes(一些我实现在插件中保持兼容的东西)中,现在你将也拥有 :path_names 选项改变你actions的名字。
map.resource :schools, :as => 'escolas', :path_names => { :new => 'nova' }
当然,我的插件当继续对早期的Rails版本有用。
定义你的ROUTES文件地址在Rails 2.1你可以定义你的routes存在哪一个文件中,包含以下行在你的environment.rb文件中:
config.routes_configuration_file
这将有用于当你拥有两种分开的前端共享相同models时,libraries 和 plugins.
例如,getsatisfaction.com 和 api.getsatisfaction.com 共用相同的 models, 但使用不同的 controllers, helpers 和 views.getsatisfaction 拥有它自己针对SEO优化的routes文件,但API routes不需要任何关于SEO的优化。
SESSION(:ON)或许你还不知道这个,Rails可以关闭sessions:
class ApplicationController < ActionController::Base
session :off
end
注意在我的救命中,我关闭了所有 controllers中的session(ApplicationController),但我也能单独关闭某一个controller的Session.
但如果我只想打开一个controller的session, 在rails 2.1中,该方法允许 :on 选项,这样做:
class UsersController < ApplicationController
session :on
end
简单的TESTING HELPERS
Rails 早期版本的确 helpers 是一个非常烦人的事。我早已经非常痛苦的保证 100% 覆盖,创建tests为一些 helpers.
由于 ActionView::TestCase类,在rails 2.1中这变得简单得多了。看个示例:
module PeopleHelper
def title(text)
content_tag(:h1, text)
end
def homepage_path
people_path
end
end
现在看我们在Rails2.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