1 如何載入核心擴充套件
1.1 獨立式 Active Support
為了儘可能減少預設佔用空間,Active Support 預設載入最小依賴項。它被分成小塊,以便只能載入所需的擴充套件。它還具有一些方便的入口點,可以一次性載入相關擴充套件,甚至是所有內容。
因此,在一個簡單的要求之後:
require "active_support"
僅載入 Active Support 框架所需的擴充套件。
1.1.1 挑選定義
此示例顯示如何載入 Hash#with_indifferent_access
。此擴充套件允許將 Hash
轉換為 ActiveSupport::HashWithIndifferentAccess
,允許以字串或 symbols 的形式訪問 keys。
{a: 1}.with_indifferent_access["a"] # => 1
對於定義為核心擴充套件的每個方法,本指南都有一個註釋,說明在哪裡定義了這樣的方法。在 with_indifferent_access
的情況下,註釋如下:
在 active_support/core_ext/hash/indifferent_access.rb
中定義。
這意味著您可以像這樣要求它:
require "active_support"
require "active_support/core_ext/hash/indifferent_access"
Active Support 已經過仔細修改,以便挑選檔案僅載入嚴格需要的依賴項(如果有)。
1.1.2 載入分組核心擴充套件
下一級是簡單地將所有擴充套件載入到 Hash
。根據經驗,可以透過載入 active_support/core_ext/some_class
一次性獲得對 SomeClass
的擴充套件。
因此,要將所有擴充套件載入到 Hash
(包括 with_indifferent_access
):
require "active_support"
require "active_support/core_ext/hash"
1.1.3 載入所有核心擴充套件
你可能更喜歡只加載所有核心擴充套件,有一個檔案:
require "active_support"
require "active_support/core_ext"
1.1.4 載入所有 Active Support
最後,如果您想讓所有 Active Support 可用,只需發出:
require "active_support/all"
這甚至沒有將整個 Active Support 預先放在記憶體中,有些東西是透過 autoload
設定的,所以只有在使用時才會載入。
1.2 Active Support 在 Ruby on Rails 應用程式中
除非 config.active_support.bare
為真,否則 Rails 應用程式上的 Ruby 會載入所有 Active Support。在這種情況下,應用程式將只加載框架本身根據自己的需要挑選的內容,並且仍然可以在任何粒度級別挑選自己,如上一節所述。
2 所有物件的擴充套件
2.1 blank?
和 present?
以下 values 在 Rails 應用程式中被視為空白:
nil
和false
,僅由空格組成的字串(見下面的註釋),
空陣列和雜湊,以及
任何其他回應
empty?
且為空的物件。
資訊:字串的謂詞使用可識別 Unicode 的字元類 [:space:]
,因此例如 U+2029(段落分隔符)被認為是空格。
警告:請注意,未提及數字。特別是,0 和 0.0 是 not 空白。
例如,來自 ActionController::HttpAuthentication::Token::ControllerMethods
的這個方法使用 [blank?
][Object#blank?] 來檢查 token 是否存在:
def authenticate(controller, &login_procedure)
token, options = token_and_options(controller.request)
unless token.blank?
login_procedure.call(token, options)
end
end
方法 [present?
][Object#present?] 等價於 !blank?
。此示例取自 ActionDispatch::Http::Cache::Response
:
def set_conditional_cache_control!
return if self["Cache-Control"].present?
# ...
end
在 active_support/core_ext/object/blank.rb
中定義。
2.2 presence
如果 present?
,則 presence
方法返回其接收者,否則返回 nil
。它對像這樣的習語很有用:
host = config[:host].presence || 'localhost'
在 active_support/core_ext/object/blank.rb
中定義。
2.3 duplicable?
從 Ruby 2.5 開始,大多數物件都可以透過 dup
或 clone
複製:
"foo".dup # => "foo"
"".dup # => ""
Rational(1).dup # => (1/1)
Complex(0).dup # => (0+0i)
1.method(:+).dup # => TypeError (allocator undefined for Method)
Active Support 提供 duplicable?
來查詢一個物件:
"foo".duplicable? # => true
"".duplicable? # => true
Rational(1).duplicable? # => true
Complex(1).duplicable? # => true
1.method(:+).duplicable? # => false
警告:任何類都可以透過刪除 dup
和 clone
或從中引發異常來禁止重複。因此只有 rescue
可以判斷給定的任意物件是否可複製。 duplicable?
依賴於上面的硬編碼列表,但它比 rescue
快得多。僅當您知道硬編碼列表在您的用例中就足夠了時才使用它。
在 active_support/core_ext/object/duplicable.rb
中定義。
2.4 deep_dup
[deep_dup
][Object#deep_dup] 方法返回給定物件的深層副本。通常,當您在 dup
一個物件中包含其他物件時,Ruby 不會 dup
它們,因此它會建立該物件的淺複製。例如,如果您有一個帶有字串的陣列,它將如下所示:
array = ['string']
duplicate = array.dup
duplicate.push 'another-string'
# 物件被複制,所以元素只新增到副本中
array # => ['string']
duplicate # => ['string', 'another-string']
duplicate.first.gsub!('string', 'foo')
# 第一個元素沒有重複,它將在兩個陣列中更改
array # => ['foo']
duplicate # => ['foo', 'another-string']
如您所見,複製 Array
實例後,我們得到了另一個物件,因此我們可以對其進行修改,原始物件保持不變。然而,對於陣列的元素而言,情況並非如此。由於 dup
沒有做深複製,數組裡面的字串還是同一個物件。
如果你需要一個物件的深複製,你應該使用 deep_dup
。下面是一個例子:
array = ['string']
duplicate = array.deep_dup
duplicate.first.gsub!('string', 'foo')
array # => ['string']
duplicate # => ['foo']
如果物件不可複製,deep_dup
只會返回它:
number = 1
duplicate = number.deep_dup
number.object_id == duplicate.object_id # => true
在 active_support/core_ext/object/deep_dup.rb
中定義。
2.5 try
當您想僅在物件不是 nil
時呼叫該物件的方法時,實現它的最簡單方法是使用條件語句,從而增加不必要的混亂。另一種方法是使用 [try
][Object#try]。 try
與 Object#send
類似,只是它如果傳送到 nil
則返回 nil
。
下面是一個例子:
# 沒有嘗試
unless @number.nil?
@number.next
end
# 嘗試
@number.try(:next)
另一個例子是來自 ActiveRecord::ConnectionAdapters::AbstractAdapter
的這段程式碼,其中 @logger
可以是 nil
。可以看到程式碼使用了try
,避免了不必要的檢查。
def log_info(sql, name, ms)
if @logger.try(:debug?)
name = '%s (%.1fms)' % [name || 'SQL', ms]
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
end
end
try
也可以不帶引數呼叫,只調用一個塊,只有當物件不為 nil 時才會執行:
@person.try { |p| "#{p.first_name} #{p.last_name}" }
請注意,try
將吞下無方法錯誤,而是返回 nil。如果您想防止輸入錯誤,請改用 [try!
][Object#try!]:
@number.try(:nest) # => nil
@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer
在 active_support/core_ext/object/try.rb
中定義。
2.6 class_eval(*args, &block)
您可以使用 [class_eval
][Kernel#class_eval] 在任何物件的單例類的上下文中評估程式碼:
class Proc
def bind(object)
block, time = self, Time.current
object.class_eval do
method_name = "__bind_#{time.to_i}_#{time.usec}"
define_method(method_name, &block)
method = instance_method(method_name)
remove_method(method_name)
method
end.bind(object)
end
end
在 active_support/core_ext/kernel/singleton_class.rb
中定義。
2.7 acts_like?(duck)
方法 acts_like?
提供了一種根據簡單約定來檢查某個類是否像其他類一樣的方法:提供與 String
定義的相同介面的類
def acts_like_string?
end
這只是一個標記,它的主體或返回 value 無關緊要。然後,客戶端程式碼可以透過這種方式查詢duck-type-safeness:
some_klass.acts_like?(:string)
Rails 有類似 Date
或 Time
的類,並遵循這個契約。
在 active_support/core_ext/object/acts_like.rb
中定義。
2.8 to_param
Rails 中的所有物件都回應方法 to_param
,該方法的目標在於返回將它們表示為查詢字串中的 values 或 URL 片段的內容。
預設情況下,to_param
只調用 to_s
:
7.to_param # => "7"
to_param
的返回 value 應該不被轉義:
"Tom & Jerry".to_param # => "Tom & Jerry"
Rails 中的幾個類覆蓋了這個方法。
例如 nil
、true
和 false
返回自身。 Array#to_param
對元素呼叫 to_param
並將結果與“/”連線:
[0, true, String].to_param # => "0/true/String"
值得注意的是,Rails 路由系統在 models 上呼叫 to_param
以獲得 :id
佔位符的 value。 ActiveRecord::Base#to_param
返回模型的 id
,但您可以在 models 中重新定義該方法。例如,給定
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
我們得到:
user_path(@user) # => "/users/357-john-smith"
警告。 Controller 需要注意對 to_param
的任何重新定義,因為當這樣的請求進入“357-john-smith”時是 params[:id]
的 value。
在 active_support/core_ext/object/to_param.rb
中定義。
2.9 to_query
to_query
方法構造一個查詢字串,該字串將給定的 key
與 to_param
的返回 value 相關聯。例如,使用以下 to_param
定義:
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
我們得到:
current_user.to_query('user') # => "user=357-john-smith"
此方法轉義了 key 和 value 所需的任何內容:
account.to_query('company[name]')
# => "company%5Bname%5D=Johnson+%26+Johnson"
因此它的輸出已準備好用於查詢字串。
陣列返回將to_query
應用於每個帶有key[]
的元素的結果為key,並用“&”連線結果:
[3.4, -45.6].to_query('sample')
# => "樣本%5B%5D=3.4&樣本%5B%5D=-45.6"
雜湊也回應 to_query
,但具有不同的簽名。如果沒有傳遞引數,呼叫會生成一系列排序的 key/值分配,在其 values 上呼叫 to_query(key)
。然後用“&”連線結果:
{c: 3, b: 2, a: 1}.to_query # => "a=1&b=2&c=3"
方法 Hash#to_query
接受 keys 的可選名稱空間:
{id: 89, name: "John Smith"}.to_query('user')
# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"
在 active_support/core_ext/object/to_query.rb
中定義。
2.10 with_options
方法 with_options
提供了一種在一系列方法呼叫中提取常見選項的方法。
給定預設選項雜湊,with_options
為塊生成代理物件。在塊內,代理上呼叫的方法被轉發到接收器,併合並了它們的選項。例如,你擺脫了重複:
class Account < ApplicationRecord
has_many :customers, dependent: :destroy
has_many :products, dependent: :destroy
has_many :invoices, dependent: :destroy
has_many :expenses, dependent: :destroy
end
這邊走:
class Account < ApplicationRecord
with_options dependent: :destroy do |assoc|
assoc.has_many :customers
assoc.has_many :products
assoc.has_many :invoices
assoc.has_many :expenses
end
end
這個習語也可以向讀者傳達分組。例如,假設您要傳送其語言取決於使用者的時事通訊。在郵件程式的某個地方,您可以像這樣對依賴於語言環境的位進行分組:
I18n.with_options locale: user.locale, scope: "newsletter" do |i18n|
subject i18n.t :subject
body i18n.t :body, user_name: user.name
end
提示:由於 with_options
將呼叫轉發給其接收者,因此它們可以巢狀。除了自己的巢狀級別外,每個巢狀級別還將合併繼承的預設值。
在 active_support/core_ext/object/with_options.rb
中定義。
2.11 JSON 支援
Active Support 提供了比 json
gem 通常為 Ruby 物件提供的更好的 to_json
實現。這是因為某些類,如 Hash
和 Process::Status
需要特殊處理才能提供正確的 JSON 表示。
在 active_support/core_ext/object/json.rb
中定義。
2.12 實例變數
Active Support 提供了幾種方法來簡化對實例變數的訪問。
2.12.1 instance_values
方法 instance_values
返回一個雜湊值,將沒有“@”的實例變數名稱對映到它們的
對應的 values。 Keys 是字串:
class C
def initialize(x, y)
@x, @y = x, y
end
end
C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
在 active_support/core_ext/object/instance_variables.rb
中定義。
2.12.2 instance_variable_names
方法 instance_variable_names
返回一個數組。每個名稱都包含“@”符號。
class C
def initialize(x, y)
@x, @y = x, y
end
end
C.new(0, 1).instance_variable_names # => ["@x", "@y"]
在 active_support/core_ext/object/instance_variables.rb
中定義。
2.13 靜默警告和異常
方法 [silence_warnings
][Kernel#silence_warnings] 和 enable_warnings
相應地更改了 $VERBOSE
的 value 的塊持續時間,然後將其重置:
silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
[suppress
][Kernel#suppress] 也可以消除異常。此方法接收任意數量的異常類。如果在塊執行期間引發異常並且是 kind_of?
的任何引數,則 suppress
捕獲它並靜默返回。否則不捕獲異常:
# 如果使用者被鎖定,增量丟失,沒什麼大不了的。
suppress(ActiveRecord::StaleObjectError) do
current_user.increment! :visits
end
在 active_support/core_ext/kernel/reporting.rb
中定義。
2.14 in?
謂詞 [in?
][Object#in?] 測試一個物件是否包含在另一個物件中。如果傳遞的引數沒有回應 include?
,則會引發 ArgumentError
異常。
in?
的示例:
1.in?([1,2]) # => true
"lo".in?("hello") # => true
25.in?(30..50) # => false
1.in?(1) # => ArgumentError
在 active_support/core_ext/object/inclusion.rb
中定義。
3 Module
的擴充套件
3.1 屬性
3.1.1 alias_attribute
Model 屬性有一個讀者、一個作者和一個謂詞。您可以使用 alias_attribute
為模型屬性設定別名,該屬性為您定義了相應的三種方法。與其他別名方法一樣,新名稱是第一個引數,舊名稱是第二個引數(一個助記符是它們的順序與您進行賦值的順序相同):
class User < ApplicationRecord
# You can refer to the email column as "login".
# This can be meaningful for authentication code.
alias_attribute :login, :email
end
在 active_support/core_ext/module/aliasing.rb
中定義。
3.1.2 內部屬性
當您在一個的目標在於被子類化的類中定義屬性時,名稱衝突是一種風險。這對圖書館來說非常重要。
Active Support 定義了宏 attr_internal_reader
、attr_internal_writer
和 [attr_internal_accessor
][attr_internal_accessor
][ZHTW_WTHZ#][ZHTW_internal_attr#]它們的行為與 Ruby 內建的 attr_*
對應物類似,只是它們以一種降低衝突可能性的方式命名底層實例變數。
宏 attr_internal
是 attr_internal_accessor
的同義詞:
# 圖書館
class ThirdPartyLibrary::Crawler
attr_internal :log_level
end
# 客戶端程式碼
class MyCrawler < ThirdPartyLibrary::Crawler
attr_accessor :log_level
end
在前面的例子中,可能是 :log_level
不屬於庫的公共介面,它只用於開發。客戶端程式碼不知道潛在的衝突,子類化並定義了自己的 :log_level
。感謝 attr_internal
沒有碰撞。
預設情況下,內部實例變數使用前導下劃線命名,在上例中為 @_log_level
。不過,這可以透過 Module.attr_internal_naming_format
進行設定,您可以傳遞任何類似 sprintf
的格式字串,並在某處使用前導 @
和 %s
,這是放置名稱的位置。預設值為 "@_%s"
。
Rails 在一些地方使用內部屬性,例如 views:
module ActionView
class Base
attr_internal :captures
attr_internal :request, :layout
attr_internal :controller, :template
end
end
在 active_support/core_ext/module/attr_internal.rb
中定義。
3.1.3 Module 屬性
宏 mattr_reader
、mattr_writer
和 mattr_accessor
與 ZTH_3W 類定義的宏相同。事實上,cattr_*
宏只是 mattr_*
宏的別名。檢查類屬性。
比如Active Storage的logger的API是用mattr_accessor
生成的:
module ActiveStorage
mattr_accessor :logger
end
在 active_support/core_ext/module/attribute_accessors.rb
中定義。
3.2 家長
3.2.1 module_parent
巢狀命名模組上的 module_parent
方法返回包含其相應常量的模組:
module X
module Y
module Z
end
end
end
M = X::Y::Z
X::Y::Z.module_parent # => X::Y
M.module_parent # => X::Y
如果 module 是匿名的或屬於頂級,則 module_parent
返回 Object
。
警告:請注意,在這種情況下 module_parent_name
返回 nil
。
在 active_support/core_ext/module/introspection.rb
中定義。
3.2.2 module_parent_name
巢狀命名模組上的 module_parent_name
方法返回包含其相應常量的模組的完全限定名稱:
module X
module Y
module Z
end
end
end
M = X::Y::Z
X::Y::Z.module_parent_name # => "X::Y"
M.module_parent_name # => "X::Y"
對於頂級或匿名 modules module_parent_name
返回 nil
。
警告:請注意,在這種情況下 module_parent
返回 Object
。
在 active_support/core_ext/module/introspection.rb
中定義。
3.2.3 module_parents
方法 module_parents
在接收器上呼叫 module_parent
並向上呼叫,直到達到 Object
。鏈以陣列形式返回,從下到上:
module X
module Y
module Z
end
end
end
M = X::Y::Z
X::Y::Z.module_parents # => [X::Y, X, Object]
M.module_parents # => [X::Y, X, Object]
在 active_support/core_ext/module/introspection.rb
中定義。
3.3 匿名
module 可能有也可能沒有名稱:
module M
end
M.name # => "M"
N = Module.new
N.name # => "N"
Module.new.name # => nil
您可以檢查模組是否具有帶有謂詞 anonymous?
的名稱:
module M
end
M.anonymous? # => false
Module.new.anonymous? # => true
請注意,無法訪問並不意味著匿名:
module M
end
m = Object.send(:remove_const, :M)
m.anonymous? # => false
儘管根據定義無法訪問匿名 module。
在 active_support/core_ext/module/anonymous.rb
中定義。
3.4 方法委託
3.4.1 delegate
宏 delegate
提供了一種簡單的方法來轉發方法。
假設某些應用程式中的使用者在 User
model 中有登入資訊,但在單獨的 Profile
model 中有姓名和其他資料:
class User < ApplicationRecord
has_one :profile
end
使用該設定,您可以透過使用者的個人資料 user.profile.name
獲得使用者名稱,但仍然能夠直接訪問此類屬性可能會很方便:
class User < ApplicationRecord
has_one :profile
def name
profile.name
end
end
這就是 delegate
為您所做的:
class User < ApplicationRecord
has_one :profile
delegate :name, to: :profile
end
它更短,意圖更明顯。
該方法在目標中必須是公共的。
delegate
宏接受幾種方法:
delegate :name, :age, :address, :twitter, to: :profile
當插入到一個字串中時,:to
選項應該成為一個表示式,該表示式計算方法被委託給的物件。通常是一個字串或 symbol。這樣的表示式是在接收者的上下文中計算的:
# 委託給 Rails 常量
delegate :logger, to: :Rails
# 委託給接收者的類
delegate :table_name, to: :class
警告:如果 :prefix
選項是 true
,這不太通用,請參見下文。
預設情況下,如果委託引發 NoMethodError
並且目標是 nil
,則傳播異常。您可以使用 :allow_nil
選項要求返回 nil
:
delegate :name, to: :profile, allow_nil: true
如果使用者沒有設定檔案,則使用 :allow_nil
呼叫 user.name
返回 nil
。
選項 :prefix
為生成的方法的名稱新增字首。例如,這可能有助於獲得更好的名稱:
delegate :street, to: :address, prefix: true
前面的示例生成 address_street
而不是 street
。
警告:由於在這種情況下生成的方法的名稱由目標物件和目標方法名稱組成,因此 :to
選項必須是方法名稱。
也可以設定自定義字首:
delegate :size, to: :attachment, prefix: :avatar
在前面的示例中,宏生成 avatar_size
而不是 size
。
選項 :private
更改方法範圍:
delegate :date_of_birth, to: :profile, private: true
預設情況下,委託方法是公共的。透過 private: true
來改變它。
定義在 active_support/core_ext/module/delegation.rb
3.4.2 delegate_missing_to
想象一下,您想委託 User
物件中缺少的所有內容,
到 Profile
之一。 delegate_missing_to
宏讓你實現這個
在微風中:
class User < ApplicationRecord
has_one :profile
delegate_missing_to :profile
end
目標可以是物件內的任何可呼叫物件,例如實例變數, 方法、常量等。僅委託目標的公共方法。
在 active_support/core_ext/module/delegation.rb
中定義。
3.5 重新定義方法
在某些情況下,您需要使用 define_method
定義一個方法,但不知道該名稱的方法是否已經存在。如果是,則在啟用它們時發出警告。沒什麼大不了的,但也不乾淨。
方法 redefine_method
可以防止這種潛在的警告,如果需要,在之前刪除現有方法。
如果需要定義,也可以使用 silence_redefinition_of_method
自己的替換方法(因為您使用的是 delegate
,對於
例子)。
在 active_support/core_ext/module/redefine_method.rb
中定義。
4 Class
的擴充套件
4.1 類屬性
4.1.1 class_attribute
方法 class_attribute
聲明瞭一個或多個可繼承的類屬性,這些屬性可以在層次結構的任何級別被覆蓋。
class A
class_attribute :x
end
class B < A; end
class C < B; end
A.x = :a
B.x # => :a
C.x # => :a
B.x = :b
A.x # => :a
C.x # => :b
C.x = :c
A.x # => :a
B.x # => :b
例如 ActionMailer::Base
定義:
class_attribute :default_params
self.default_params = {
mime_version: "1.0",
charset: "UTF-8",
content_type: "text/plain",
parts_order: [ "text/plain", "text/enriched", "text/html" ]
}.freeze
也可以在實例級別訪問和覆蓋它們。
A.x = 1
a1 = A.new
a2 = A.new
a2.x = 2
a1.x # => 1, comes from A
a2.x # => 2, overridden in a2
可以透過將選項 :instance_writer
設定為 false
來阻止編寫器實例方法的生成。
module ActiveRecord
class Base
class_attribute :table_name_prefix, instance_writer: false, default: "my"
end
end
model 可能會發現該選項可用作防止批次分配設定屬性的方法。
可以透過將選項 :instance_reader
設定為 false
來阻止讀取器實例方法的生成。
class A
class_attribute :x, instance_reader: false
end
A.new.x = 1
A.new.x # NoMethodError
為方便起見,class_attribute
還定義了一個實例謂詞,它是實例讀取器返回內容的雙重否定。在上面的例子中,它被稱為 x?
。
當 :instance_reader
為 false
時,實例謂詞返回一個 NoMethodError
,就像 reader 方法一樣。
如果你不想要實例謂詞,傳遞 instance_predicate: false
並且它不會被定義。
在 active_support/core_ext/class/attribute.rb
中定義。
4.1.2 cattr_reader
、cattr_writer
和 cattr_accessor
宏 cattr_reader
、cattr_writer
和 [cattr_accessor
][Module#cattr_accessor 類] 類似於 ZTH_W 類。他們將一個類變數初始化為 nil
除非它已經存在,並生成相應的類方法來訪問它:
class MysqlAdapter < AbstractAdapter
# Generates class methods to access @@emulate_booleans.
cattr_accessor :emulate_booleans
end
此外,您可以將塊傳遞給 cattr_*
以使用預設的 value 設定屬性:
class MysqlAdapter < AbstractAdapter
# Generates class methods to access @@emulate_booleans with default value of true.
cattr_accessor :emulate_booleans, default: true
end
實例方法也是為了方便而建立的,它們只是類屬性的代理。因此,實例可以更改類屬性,但不能像 class_attribute
那樣覆蓋它(見上文)。例如給出
module ActionView
class Base
cattr_accessor :field_error_proc, default: Proc.new { ... }
end
end
我們可以在 views 中訪問 field_error_proc
。
可以透過將 :instance_reader
設定為 false
來阻止讀取器實例方法的生成,可以透過將 :instance_writer
設定為 false
來阻止寫入器實例方法的生成。可以透過將 :instance_accessor
設定為 false
來阻止這兩種方法的生成。在所有情況下,value 必須完全是 false
而不是任何虛假的 value。
module A
class B
# No first_name instance reader is generated.
cattr_accessor :first_name, instance_reader: false
# No last_name= instance writer is generated.
cattr_accessor :last_name, instance_writer: false
# No surname instance reader or surname= writer is generated.
cattr_accessor :surname, instance_accessor: false
end
end
model 可能會發現將 :instance_accessor
設定為 false
作為防止批次分配設定屬性的方法很有用。
在 active_support/core_ext/module/attribute_accessors.rb
中定義。
4.2 子類和後代
4.2.1 subclasses
subclasses
方法返回接收者的子類:
class C; end
C.subclasses # => []
class B < C; end
C.subclasses # => [B]
class A < B; end
C.subclasses # => [B]
class D < C; end
C.subclasses # => [B, D]
未指定這些類的返回順序。
在 active_support/core_ext/class/subclasses.rb
中定義。
4.2.2 descendants
descendants
方法返回所有比其接收者為 <
的類:
class C; end
C.descendants # => []
class B < C; end
C.descendants # => [B]
class A < B; end
C.descendants # => [B, A]
class D < C; end
C.descendants # => [B, A, D]
未指定這些類的返回順序。
在 active_support/core_ext/class/subclasses.rb
中定義。
5 String
的擴充套件
5.1 輸出安全
5.1.1 動機
將資料插入 HTML 模板需要格外小心。例如,您不能將 @review.title
逐字插入 HTML 頁面。一方面,如果 review 的標題是“Flanagan & Matz 規則!”輸出不會是格式正確的,因為必須將與號轉義為“&”。此外,根據應用程式,這可能是一個很大的安全漏洞,因為使用者可以將惡意 HTML 設定為手工製作的 review 標題。檢視安全指南 中有關跨站點指令碼的部分,瞭解有關風險的更多資訊。
5.1.2 安全字串
Active Support 有(html) 安全字串的概念。安全字串是標記為可按原樣插入 HTML 的字串。它是可信的,不管它是否被逃脫。
預設情況下,字串被認為是 unsafe :
"".html_safe? # => false
您可以使用 html_safe
方法從給定的字串中獲取安全字串:
s = "".html_safe
s.html_safe? # => true
重要的是要理解 html_safe
不執行任何轉義,它只是一個斷言:
s = "<script>...</script>".html_safe
s.html_safe? # => true
s # => "<script>...</script>"
您有責任確保在特定字串上呼叫 html_safe
沒有問題。
如果您附加到安全字串,無論是使用 concat
/<<
就地,還是使用 +
,結果都是安全字串。不安全的引數被轉義:
"".html_safe + "<" # => "<"
直接附加安全引數:
"".html_safe + "<".html_safe # => "<"
這些方法不應該在普通的views中使用。不安全的 values 會自動轉義:
<%= @review.title %> <%# fine, escaped if needed %>
要逐字插入內容,請使用 raw
helper 而不是呼叫 html_safe
:
<%= raw @cms.current_template %> <%# inserts @cms.current_template as is %>
或者,等效地,使用 <%==
:
<%== @cms.current_template %> <%# inserts @cms.current_template as is %>
raw
helper 為您呼叫 html_safe
:
def raw(stringish)
stringish.to_s.html_safe
end
在 active_support/core_ext/string/output_safety.rb
中定義。
5.1.3 改造
根據經驗,除了上面解釋的連線之外,任何可能改變字串的方法都會給你一個不安全的字串。它們是 downcase
、gsub
、strip
、chomp
、underscore
等。
在像 gsub!
這樣的就地轉換的情況下,接收器本身變得不安全。
資訊:安全位總是丟失,無論轉換是否真的改變了某些東西。
5.1.4 轉換和強制
對安全字串呼叫 to_s
返回安全字串,但對 to_str
進行強制返回不安全字串。
5.1.5 複製
在安全字串上呼叫 dup
或 clone
會產生安全字串。
5.2 remove
方法 remove
將刪除所有出現的模式:
"Hello World".remove(/Hello /) # => "World"
還有破壞性版本String#remove!
。
在 active_support/core_ext/string/filters.rb
中定義。
5.3 squish
方法 squish
去除前導和尾隨空格,並用一個空格替換一行空格:
" \n foo\n\r \t bar \n".squish # => "foo bar"
還有破壞性版本String#squish!
。
請注意,它同時處理 ASCII 和 Unicode 空格。
在 active_support/core_ext/string/filters.rb
中定義。
5.4 truncate
方法 truncate
返回在給定的 length
之後截斷的接收者的副本:
"Oh dear! Oh dear! I shall be late!".truncate(20)
# => “哦,天哪!天哪!...”
可以使用 :omission
選項自定義省略號:
"Oh dear! Oh dear! I shall be late!".truncate(20, omission: '…')
# => “哦,天哪!哦……”
請特別注意,截斷考慮了省略字串的長度。
傳遞一個 :separator
以在自然中斷處截斷字串:
"Oh dear! Oh dear! I shall be late!".truncate(18)
# => “哦,天哪!哦,死……”
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: ' ')
# => “哦,天哪!哦……”
選項 :separator
可以是正則表示式:
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: /\s/)
# => “哦,天哪!哦……”
在上面的例子中,“親愛的”首先被切斷,但隨後 :separator
阻止了它。
在 active_support/core_ext/string/filters.rb
中定義。
5.5 truncate_bytes
方法 truncate_bytes
返回其接收器的副本,最多被截斷為 bytesize
位元組:
"👍👍👍👍".truncate_bytes(15)
# => "👍👍👍…"
可以使用 :omission
選項自定義省略號:
"👍👍👍👍".truncate_bytes(15, omission: "🖖")
# => "👍👍🖖"
在 active_support/core_ext/string/filters.rb
中定義。
5.6 truncate_words
方法 truncate_words
返回在給定單詞數後截斷的接收者副本:
"Oh dear! Oh dear! I shall be late!".truncate_words(4)
# => “哦,天哪!天哪!...”
可以使用 :omission
選項自定義省略號:
"Oh dear! Oh dear! I shall be late!".truncate_words(4, omission: '…')
# => “天哪!天哪!……”
傳遞一個 :separator
以在自然中斷處截斷字串:
"Oh dear! Oh dear! I shall be late!".truncate_words(3, separator: '!')
# => “哦,天哪!天哪!我要遲到了……”
選項 :separator
可以是正則表示式:
"Oh dear! Oh dear! I shall be late!".truncate_words(4, separator: /\s/)
# => “哦,天哪!天哪!...”
在 active_support/core_ext/string/filters.rb
中定義。
5.7 inquiry
inquiry
方法將字串轉換為 StringInquirer
物件,使相等性檢查更漂亮。
"production".inquiry.production? # => true
"active".inquiry.inactive? # => false
在 active_support/core_ext/string/inquiry.rb
中定義。
5.8 starts_with?
和 ends_with?
Active Support 定義了 String#start_with?
和 String#end_with?
的第三人稱別名:
"foo".starts_with?("f") # => true
"foo".ends_with?("o") # => true
在 active_support/core_ext/string/starts_ends_with.rb
中定義。
5.9 strip_heredoc
方法 strip_heredoc
去除了heredocs 中的縮排。
例如在
if options[:usage]
puts <<-USAGE.strip_heredoc
This command does such and such.
Supported options are:
-h This message
...
USAGE
end
使用者將看到與左邊距對齊的使用訊息。
從技術上講,它會查詢整個字串中縮排最少的行,並刪除 大量的領先空白。
在 active_support/core_ext/string/strip.rb
中定義。
5.10 indent
indent
方法縮排接收器中的行:
<<EOS.indent(2)
def some_method
some_code
end
EOS
# =>
def some_method
some_code
end
第二個引數 indent_string
指定要使用的縮排字串。預設值是 nil
,它告訴方法在第一個縮排的行中進行有根據的猜測,如果沒有,則回退到一個空格。
" foo".indent(2) # => " foo"
"foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
"foo".indent(2, "\t") # => "\t\tfoo"
雖然 indent_string
通常是一個空格或製表符,但它可以是任何字串。
第三個引數 indent_empty_lines
是一個標誌,表示是否應該縮排空行。預設為假。
"foo\n\nbar".indent(2) # => " foo\n\n bar"
"foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
indent!
方法執行就地縮排。
在 active_support/core_ext/string/indent.rb
中定義。
5.11 訪問
5.11.1 at(position)
at
方法返回字串在 position
位置的字元:
"hello".at(0) # => "h"
"hello".at(4) # => "o"
"hello".at(-1) # => "o"
"hello".at(10) # => nil
在 active_support/core_ext/string/access.rb
中定義。
5.11.2 from(position)
from
方法返回從位置 position
開始的字串的子字串:
"hello".from(0) # => "hello"
"hello".from(2) # => "llo"
"hello".from(-2) # => "lo"
"hello".from(10) # => nil
在 active_support/core_ext/string/access.rb
中定義。
5.11.3 to(position)
to
方法返回字串的子字串,直到位置 position
:
"hello".to(0) # => "h"
"hello".to(2) # => "hel"
"hello".to(-2) # => "hell"
"hello".to(10) # => "hello"
在 active_support/core_ext/string/access.rb
中定義。
5.11.4 first(limit = 1)
first
方法返回包含字串的第一個 limit
字元的子字串。
如果 n
> 0,則呼叫 str.first(n)
等價於 str.to(n-1)
,如果 n
== 0,則返回空字串。
在 active_support/core_ext/string/access.rb
中定義。
5.11.5 last(limit = 1)
last
方法返回包含字串的最後一個 limit
字元的子字串。
如果 n
> 0,則呼叫 str.last(n)
等價於 str.from(-n)
,如果 n
== 0,則返回空字串。
在 active_support/core_ext/string/access.rb
中定義。
5.12 拐點
5.12.1 pluralize
方法 pluralize
返回其接收者的複數:
"table".pluralize # => "tables"
"ruby".pluralize # => "rubies"
"equipment".pluralize # => "equipment"
如上例所示,Active Support 知道一些不規則複數和不可數名詞。內建規則可以在 config/initializers/inflections.rb
中擴充套件。預設情況下,該檔案由 rails new
命令生成,並在註釋中包含說明。
pluralize
也可以採用可選的 count
引數。如果 count == 1
將返回單數形式。對於 count
的任何其他 value,將返回複數形式:
"dude".pluralize(0) # => "dudes"
"dude".pluralize(1) # => "dude"
"dude".pluralize(2) # => "dudes"
Active Record 使用此方法計算對應於 model 的預設表名:
# active_record/model_schema.rb
def undecorated_table_name(model_name)
table_name = model_name.to_s.demodulize.underscore
pluralize_table_names ? table_name.pluralize : table_name
end
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.2 singularize
singularize
方法是 pluralize
的逆方法:
"tables".singularize # => "table"
"rubies".singularize # => "ruby"
"equipment".singularize # => "equipment"
Associations 使用此方法計算相應預設關聯類的名稱:
# active_record/reflection.rb
def derive_class_name
class_name = name.to_s.camelize
class_name = class_name.singularize if collection?
class_name
end
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.3 camelize
方法 camelize
以駝峰形式返回其接收器:
"product".camelize # => "Product"
"admin_user".camelize # => "AdminUser"
根據經驗,您可以將此方法視為將路徑轉換為 Ruby 類或 module 名稱的方法,其中斜線分隔名稱空間:
"backoffice/session".camelize # => "Backoffice::Session"
例如,Action Pack 使用此方法載入提供某個 session 儲存的類:
# action_controller/metal/session_management.rb
def session_store=(store)
@@session_store = store.is_a?(Symbol) ?
ActionDispatch::Session.const_get(store.to_s.camelize) :
store
end
camelize
接受一個可選引數,它可以是 :upper
(預設)或 :lower
。對於後者,第一個字母變為小寫:
"visual_effect".camelize(:lower) # => "visualEffect"
這對於以遵循該約定的語言(例如 JavaScript)計算方法名稱可能很方便。
資訊:根據經驗,您可以將 camelize
視為 underscore
的倒數,儘管在某些情況下不成立:"SSLError".underscore.camelize
返回 "SslError"
。為了支援這種情況,Active Support 允許您在 config/initializers/inflections.rb
中指定首字母縮略詞:
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym 'SSL'
end
"SSLError".underscore.camelize # => "SSLError"
camelize
別名為 camelcase
。
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.4 underscore
方法 underscore
反過來,從駝峰式到路徑:
"Product".underscore # => "product"
"AdminUser".underscore # => "admin_user"
還將 "::" 轉換回 "/":
"Backoffice::Session".underscore # => "backoffice/session"
並理解以小寫字母開頭的字串:
"visualEffect".underscore # => "visual_effect"
underscore
不接受任何引數。
Rails 使用 underscore
來獲取 controller 類的小寫名稱:
# actionpack/lib/abstract_controller/base.rb
def controller_path
@controller_path ||= name.delete_suffix("Controller").underscore
end
例如,value 就是您在 params[:controller]
中得到的那個。
資訊:根據經驗,您可以將 underscore
視為 camelize
的倒數,儘管在某些情況下並不成立。例如,"SSLError".underscore.camelize
返還 "SslError"
。
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.5 titleize
方法 titleize
將接收器中的單詞大寫:
"alice in wonderland".titleize # => "Alice In Wonderland"
"fermat's enigma".titleize # => "Fermat's Enigma"
titleize
別名為 titlecase
。
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.6 dasherize
方法 dasherize
用破折號替換接收器中的下劃線:
"name".dasherize # => "name"
"contact_data".dasherize # => "contact-data"
models 的 XML 序列化程式使用此方法對節點名稱進行破折:
# active_model/serializers/xml.rb
def reformat_name(name)
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
end
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.7 demodulize
給定一個帶有限定常量名的字串,demodulize
返回常量名,即最右邊的部分:
"Product".demodulize # => "Product"
"Backoffice::UsersController".demodulize # => "UsersController"
"Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils"
"::Inflections".demodulize # => "Inflections"
"".demodulize # => ""
例如,Active Record 使用此方法計算計數器快取列的名稱:
# active_record/reflection.rb
def counter_cache_column
if options[:counter_cache] == true
"#{active_record.name.demodulize.underscore.pluralize}_count"
elsif options[:counter_cache]
options[:counter_cache]
end
end
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.8 deconstantize
給定一個帶有限定常量引用表示式的字串,deconstantize
刪除最右邊的部分,通常保留常量容器的名稱:
"Product".deconstantize # => ""
"Backoffice::UsersController".deconstantize # => "Backoffice"
"Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel"
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.9 parameterize
方法 parameterize
以一種可用於漂亮 URL 的方式標準化其接收器。
"John Smith".parameterize # => "john-smith"
"Kurt Gödel".parameterize # => "kurt-godel"
要保留字串的大小寫,請將 preserve_case
引數設定為 true。預設情況下,preserve_case
設定為 false。
"John Smith".parameterize(preserve_case: true) # => "John-Smith"
"Kurt Gödel".parameterize(preserve_case: true) # => "Kurt-Godel"
要使用自定義分隔符,請覆蓋 separator
引數。
"John Smith".parameterize(separator: "_") # => "john\_smith"
"Kurt Gödel".parameterize(separator: "_") # => "kurt\_godel"
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.10 tableize
方法 tableize
是 underscore
後跟 pluralize
。
"Person".tableize # => "people"
"Invoice".tableize # => "invoices"
"InvoiceLine".tableize # => "invoice_lines"
根據經驗,對於簡單的情況,tableize
返回對應於給定 model 的表名。 Active Record 中的實際實現確實不是直接的 tableize
,因為它還解調了類名並檢查了一些可能影響返回字串的選項。
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.11 classify
方法 classify
是 tableize
的逆方法。它為您提供與表名對應的類名:
"people".classify # => "Person"
"invoices".classify # => "Invoice"
"invoice_lines".classify # => "InvoiceLine"
該方法理解限定的表名:
"highrise_production.companies".classify # => "Company"
請注意,classify
將類名作為字串返回。您可以透過在其上呼叫 constantize
來獲取實際的類物件,接下來解釋。
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.12 constantize
方法 constantize
解析其接收器中的常量引用表示式:
"Integer".constantize # => Integer
module M
X = 1
end
"M::X".constantize # => 1
如果字串評估為沒有已知常量,或者其內容甚至不是有效的常量名稱,則 constantize
引發 NameError
。
即使沒有前導“::”,由 constantize
進行的常量名稱解析也始終從頂級 Object
開始。
X = :in_Object
module M
X = :in_M
X # => :in_M
"::X".constantize # => :in_Object
"X".constantize # => :in_Object (!)
end
所以,它通常不等同於 Ruby 在同一地點所做的,有一個真正的常數被評估。
郵件程式測試用例使用 constantize
從測試類的名稱中獲取被測試的郵件程式:
# action_mailer/test_case.rb
def determine_default_mailer(name)
name.delete_suffix("Test").constantize
rescue NameError => e
raise NonInferrableMailerError.new(name)
end
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.13 humanize
方法 humanize
調整屬性名稱以顯示給終端使用者。
具體來說,它執行這些轉換:
- 將人為屈折規則應用於論證。
- 刪除前導下劃線,如果有的話。
- 刪除“_id”字尾(如果存在)。
- 用空格替換下劃線(如果有)。
- 將除首字母縮略詞以外的所有單詞小寫。
- 首字母大寫。
可以透過設定關閉第一個單詞的大寫
:capitalize
選項為 false(預設為 true)。
"name".humanize # => "Name"
"author_id".humanize # => "Author"
"author_id".humanize(capitalize: false) # => "author"
"comments_count".humanize # => "Comments count"
"_id".humanize # => "Id"
如果將“SSL”定義為首字母縮寫詞:
'ssl_error'.humanize # => "SSL error"
helper 方法 full_messages
使用 humanize
作為後備以包含
屬性名稱:
def full_messages
map { |attribute, message| full_message(attribute, message) }
end
def full_message
# ...
attr_name = attribute.to_s.tr('.', '_').humanize
attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
# ...
end
在 active_support/core_ext/string/inflections.rb
中定義。
5.12.14 foreign_key
方法 foreign_key
給出來自類名的外部 key 列名。為此,它會解調、下劃線並新增“_id”:
"User".foreign_key # => "user_id"
"InvoiceLine".foreign_key # => "invoice_line_id"
"Admin::Session".foreign_key # => "session_id"
如果您不想在“_id”中使用下劃線,則傳遞一個錯誤引數:
"User".foreign_key(false) # => "userid"
Associations 使用這個方法來推斷國外的 keys,例如 has_one
和 has_many
這樣做:
# active_record/associations.rb
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
在 active_support/core_ext/string/inflections.rb
中定義。
5.13 轉換
5.13.1 to_date
、to_time
、to_datetime
方法 to_date
、to_time
和 to_datetime
基本上是對 Date._parse
的便利包裝:
"2010-07-27".to_date # => Tue, 27 Jul 2010
"2010-07-27 23:37:00".to_time # => 2010-07-27 23:37:00 +0200
"2010-07-27 23:37:00".to_datetime # => Tue, 27 Jul 2010 23:37:00 +0000
to_time
接收可選引數 :utc
或 :local
,以指示您希望時間在哪個時區:
"2010-07-27 23:42:00".to_time(:utc) # => 2010-07-27 23:42:00 UTC
"2010-07-27 23:42:00".to_time(:local) # => 2010-07-27 23:42:00 +0200
預設為 :local
。
有關詳細資訊,請參閱 Date._parse
的文件。
資訊:他們三個為空白接收者返回 nil
。
在 active_support/core_ext/string/conversions.rb
中定義。
6 Symbol
的擴充套件
6.1 starts_with?
和 ends_with?
Active Support 定義了 Symbol#start_with?
和 Symbol#end_with?
的第三人稱別名:
:foo.starts_with?("f") # => true
:foo.ends_with?("o") # => true
在 active_support/core_ext/symbol/starts_ends_with.rb
中定義。
7 Numeric
的擴充套件
7.1 位元組
所有數字都回應這些方法:
bytes
- [
kilobytes
][數字#千位元組] - [
megabytes
][數字#兆位元組] - [
gigabytes
][數字#gigabytes] - [
terabytes
][數字#TB] - [
petabytes
][數字#petabytes] - [
exabytes
][數字#exabytes]
它們使用 1024 的轉換因子返回相應的位元組數:
2.kilobytes # => 2048
3.megabytes # => 3145728
3.5.gigabytes # => 3758096384
-4.exabytes # => -4611686018427387904
單數形式具有別名,因此您可以說:
1.megabyte # => 1048576
在 active_support/core_ext/numeric/bytes.rb
中定義。
7.2 時間
以下方法:
啟用時間宣告和計算,如 45.minutes + 2.hours + 4.weeks
。它們的返回值 values 也可以新增到 Time 物件或從中減去。
這些方法可以結合 [from_now
][Duration#from_now]、[ago
][Duration#ago] 等進行精確的日期計算。例如:
# 相當於 Time.current.advance(days: 1)
1.day.from_now
# 相當於 Time.current.advance(weeks: 2)
2.weeks.from_now
# 相當於 Time.current.advance(days: 4, week: 5)
(4.days + 5.weeks).from_now
警告。其他時長請參考 Integer
的時間擴充套件。
在 active_support/core_ext/numeric/time.rb
中定義。
7.3 格式
以多種方式啟用數字格式。
生成一個數字的字串表示作為電話號碼:
5551234.to_s(:phone)
# => 555-1234
1235551234.to_s(:phone)
# => 123-555-1234
1235551234.to_s(:phone, area_code: true)
# => (123) 555-1234
1235551234.to_s(:phone, delimiter: " ")
# => 123 555 1234
1235551234.to_s(:phone, area_code: true, extension: 555)
# => (123) 555-1234 x 555
1235551234.to_s(:phone, country_code: 1)
# => +1-123-555-1234
生成一個數字的字串表示作為貨幣:
1234567890.50.to_s(:currency) # => $1,234,567,890.50
1234567890.506.to_s(:currency) # => $1,234,567,890.51
1234567890.506.to_s(:currency, precision: 3) # => $1,234,567,890.506
以百分比形式生成數字的字串表示形式:
100.to_s(:percentage)
# => 100.000%
100.to_s(:percentage, precision: 0)
# => 100%
1000.to_s(:percentage, delimiter: '.', separator: ',')
# => 1.000,000%
302.24398923423.to_s(:percentage, precision: 5)
# => 302.24399%
以分隔形式生成數字的字串表示形式:
12345678.to_s(:delimited) # => 12,345,678
12345678.05.to_s(:delimited) # => 12,345,678.05
12345678.to_s(:delimited, delimiter: ".") # => 12.345.678
12345678.to_s(:delimited, delimiter: ",") # => 12,345,678
12345678.05.to_s(:delimited, separator: " ") # => 12,345,678 05
生成四捨五入到精度的數字的字串表示形式:
111.2345.to_s(:rounded) # => 111.235
111.2345.to_s(:rounded, precision: 2) # => 111.23
13.to_s(:rounded, precision: 5) # => 13.00000
389.32314.to_s(:rounded, precision: 0) # => 389
111.2345.to_s(:rounded, significant: true) # => 111
生成一個數字的字串表示,作為人類可讀的位元組數:
123.to_s(:human_size) # => 123 Bytes
1234.to_s(:human_size) # => 1.21 KB
12345.to_s(:human_size) # => 12.1 KB
1234567.to_s(:human_size) # => 1.18 MB
1234567890.to_s(:human_size) # => 1.15 GB
1234567890123.to_s(:human_size) # => 1.12 TB
1234567890123456.to_s(:human_size) # => 1.1 PB
1234567890123456789.to_s(:human_size) # => 1.07 EB
以人類可讀的單詞生成數字的字串表示形式:
123.to_s(:human) # => "123"
1234.to_s(:human) # => "1.23 Thousand"
12345.to_s(:human) # => "12.3 Thousand"
1234567.to_s(:human) # => "1.23 Million"
1234567890.to_s(:human) # => "1.23 Billion"
1234567890123.to_s(:human) # => "1.23 Trillion"
1234567890123456.to_s(:human) # => "1.23 Quadrillion"
在 active_support/core_ext/numeric/conversions.rb
中定義。
8 Integer
的擴充套件
8.1 multiple_of?
方法 multiple_of?
測試一個整數是否是引數的倍數:
2.multiple_of?(1) # => true
1.multiple_of?(2) # => false
在 active_support/core_ext/integer/multiple.rb
中定義。
8.2 ordinal
[ordinal
][Integer#ordinal]方法返回接收者整數對應的序數字尾字串:
1.ordinal # => "st"
2.ordinal # => "nd"
53.ordinal # => "rd"
2009.ordinal # => "th"
-21.ordinal # => "st"
-134.ordinal # => "th"
在 active_support/core_ext/integer/inflections.rb
中定義。
8.3 ordinalize
ordinalize
方法返回接收者整數對應的序數字符串。相比之下,請注意 ordinal
方法返回僅字尾字串。
1.ordinalize # => "1st"
2.ordinalize # => "2nd"
53.ordinalize # => "53rd"
2009.ordinalize # => "2009th"
-21.ordinalize # => "-21st"
-134.ordinalize # => "-134th"
在 active_support/core_ext/integer/inflections.rb
中定義。
8.4 時間
以下方法:
啟用時間宣告和計算,如 4.months + 5.years
。它們的返回值 values 也可以新增到 Time 物件或從中減去。
這些方法可以結合 [from_now
][Duration#from_now]、[ago
][Duration#ago] 等進行精確的日期計算。例如:
# 相當於 Time.current.advance(months: 1)
1.month.from_now
# 相當於 Time.current.advance(years: 2)
2.years.from_now
# 相當於 Time.current.advance(月: 4, 年: 5)
(4.months + 5.years).from_now
警告。其他時長請參考 Numeric
的時間擴充套件。
在 active_support/core_ext/integer/time.rb
中定義。
9 BigDecimal
的擴充套件
9.1 to_s
to_s
方法提供了預設說明符“F”。這意味著對 to_s
的簡單呼叫將導致浮點表示而不是工程符號:
BigDecimal(5.00, 6).to_s # => "5.0"
並且還支援 symbol 說明符:
BigDecimal(5.00, 6).to_s(:db) # => "5.0"
仍然支援工程符號:
BigDecimal(5.00, 6).to_s("e") # => "0.5E1"
10 Enumerable
的擴充套件
10.1 sum
方法 sum
新增可列舉的元素:
[1, 2, 3].sum # => 6
(1..100).sum # => 5050
新增僅假設元素回應 +
:
[[1, 2], [2, 3], [3, 4]].sum # => [1, 2, 2, 3, 3, 4]
%w(foo bar baz).sum # => "foobarbaz"
{a: 1, b: 2, c: 3}.sum # => [:a, 1, :b, 2, :c, 3]
預設情況下,空集合的總和為零,但這是可自定義的:
[].sum # => 0
[].sum(1) # => 1
如果給出了一個塊,則 sum
成為一個迭代器,它產生集合的元素並對返回的 values 求和:
(1..5).sum {|n| n * 2 } # => 30
[2, 4, 6, 8, 10].sum # => 30
空接收器的總和也可以用這種形式定製:
[].sum(1) {|n| n**3} # => 1
在 active_support/core_ext/enumerable.rb
中定義。
10.2 index_by
方法 index_by
生成一個雜湊,其中包含由某些 key 索引的可列舉元素。
它遍歷集合並將每個元素傳遞給一個塊。該元素將被塊返回的 value 處理為 key:
invoices.index_by(&:number)
# => {'2009-032' => <發票 ...>, '2009-008' => <發票 ...>, ...}
警告。 Keys 通常應該是唯一的。如果塊為不同的元素返回相同的 value,則不會為該鍵構建集合。最後一項將獲勝。
在 active_support/core_ext/enumerable.rb
中定義。
10.3 index_with
方法 index_with
使用可列舉的元素作為 keys 生成雜湊。 value
是傳遞的預設值或在塊中返回。
post = Post.new(title: "hey there", body: "what's up?")
%i( title body ).index_with { |attr_name| post.public_send(attr_name) }
# => { title: "嘿嘿", body: "怎麼了?" }
WEEKDAYS.index_with(Interval.all_day)
# => { 星期一: [ 0, 1440 ], ... }
在 active_support/core_ext/enumerable.rb
中定義。
10.4 many?
方法 many?
是 collection.size > 1
的簡寫:
<% if pages.many? %>
<%= pagination_links %>
<% end %>
如果給出了一個可選塊,many?
只考慮那些返回 true 的元素:
@see_more = videos.many? {|video| video.category == params[:category]}
在 active_support/core_ext/enumerable.rb
中定義。
10.5 exclude?
謂詞 exclude?
測試給定的物件是否不屬於集合。它是對內建 include?
的否定:
to_visit << node if visited.exclude?(node)
在 active_support/core_ext/enumerable.rb
中定義。
10.6 including
方法 including
返回一個包含傳遞元素的新列舉:
[ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
["David", "Rafael"].including %w[ Aaron Todd ] # => ["David", "Rafael", "Aaron", "Todd"]
在 active_support/core_ext/enumerable.rb
中定義。
10.7 excluding
方法 [excluding
][Enumerable# exclude] 返回具有指定元素的可列舉的副本
移除:
["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
excluding
別名為 without
。
在 active_support/core_ext/enumerable.rb
中定義。
[可列舉#排除]:https://api.rubyonrails.org/classes/Enumerable.html#method-i- exclude
10.8 pluck
方法 pluck
從每個元素中提取給定的 key:
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name) # => ["David", "Rafael", "Aaron"]
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name) # => [[1, "David"], [2, "Rafael"]]
在 active_support/core_ext/enumerable.rb
中定義。
10.9 pick
方法 pick
從第一個元素中提取給定的 key:
[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name) # => "David"
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name) # => [1, "David"]
在 active_support/core_ext/enumerable.rb
中定義。
11 Array
的擴充套件
11.1 訪問
Active Support 增加了陣列的 API 以簡化訪問它們的某些方式。例如, to
返回元素的子陣列,直到傳遞的索引處的元素:
%w(a b c d).to(2) # => ["a", "b", "c"]
[].to(7) # => []
類似地, [from
][Array#from] 返回從傳遞索引處的元素到結尾的尾部。如果索引大於陣列的長度,則返回一個空陣列。
%w(a b c d).from(2) # => ["c", "d"]
%w(a b c d).from(10) # => []
[].from(0) # => []
方法 [including
][Array#include] 返回一個包含傳遞元素的新陣列:
[ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
[ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]
方法 [excluding
][Array# exclude] 返回不包括指定元素的 Array 的副本。
這是對使用 Array#-
的 Enumerable#excluding
的最佳化
出於效能原因而不是 Array#reject
。
["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
[ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ]
方法 [second
][Array#second]、third
、fourth
和 fifth
返回相應的元素,[second_to_last
][Array 也是如此#second_to_last] 和 third_to_last
(first
和 last
是內建的)。由於社會智慧和積極的建設性,forty_two
也可用。
%w(a b c d).third # => "c"
%w(a b c d).fifth # => nil
在 active_support/core_ext/array/access.rb
中定義。
[陣列#排除]:https://api.rubyonrails.org/classes/Array.html#method-i- exclude
11.2 提取
方法 extract!
移除並返回塊返回真 value 的元素。
如果沒有給出塊,則返回一個列舉器。
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
numbers # => [0, 2, 4, 6, 8]
在 active_support/core_ext/array/extract.rb
中定義。
11.3 期權 Extraction
當方法呼叫中的最後一個引數是雜湊時,除了 &block
引數,Ruby 允許您省略括號:
User.exists?(email: params[:email])
這種語法糖在 Rails 中被大量使用,以避免出現過多的位置引數,而是提供模擬命名引數的介面。特別是使用尾隨雜湊作為選項是非常慣用的。
然而,如果一個方法需要可變數量的引數並在其宣告中使用 *
,那麼這樣的選項雜湊最終會成為引數陣列的一個專案,在那裡它失去了它的作用。
在這些情況下,您可以使用 extract_options!
對選項雜湊進行特殊處理。此方法檢查陣列最後一項的型別。如果它是一個雜湊,它彈出它並返回它,否則它返回一個空的雜湊。
例如,讓我們看看 caches_action
controller 宏的定義:
def caches_action(*actions)
return unless cache_configured?
options = actions.extract_options!
# ...
end
此方法接收任意數量的 action 名稱,以及作為最後一個引數的可選雜湊選項。透過呼叫 extract_options!
,您可以獲得選項雜湊並以簡單而明確的方式將其從 actions
中刪除。
在 active_support/core_ext/array/extract_options.rb
中定義。
11.4 轉換
11.4.1 to_sentence
方法 to_sentence
將陣列轉換為包含列舉其專案的句子的字串:
%w().to_sentence # => ""
%w(Earth).to_sentence # => "Earth"
%w(Earth Wind).to_sentence # => "Earth and Wind"
%w(Earth Wind Fire).to_sentence # => "Earth, Wind, and Fire"
此方法接受三個選項:
-
:two_words_connector
:用於長度為2的陣列。預設為“和”。 -
:words_connector
:用於連線3個或更多元素的陣列元素,除了最後兩個。預設為“,”。 -
:last_word_connector
:用於連線具有 3 個或更多元素的陣列的最後一項。預設為“,和”。
這些選項的預設值可以本地化,它們的 keys 是:
選項 | I18n key |
---|---|
:two_words_connector |
support.array.two_words_connector |
:words_connector |
support.array.words_connector |
:last_word_connector |
support.array.last_word_connector |
在 active_support/core_ext/array/conversions.rb
中定義。
11.4.2 to_formatted_s
預設情況下,方法 to_formatted_s
的作用類似於 to_s
。
但是,如果陣列包含回應 id
的專案,則 symbol
:db
可以作為引數傳遞。這通常與
Active Record 物件的集合。返回的字串是:
[].to_formatted_s(:db) # => "null"
[user].to_formatted_s(:db) # => "8456"
invoice.lines.to_formatted_s(:db) # => "23,567,556,12"
上面示例中的整數應該來自對 id
的相應呼叫。
在 active_support/core_ext/array/conversions.rb
中定義。
11.4.3 to_xml
方法 to_xml
返回一個包含其接收者的 XML 表示的字串:
Contributor.limit(2).order(:rank).to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors type="array">
# <貢獻者>
# <id type="integer">4356</id>
# <name>傑瑞米·肯珀</name>
# <rank type="integer">1</rank>
# <url-id>jeremy-kemper</url-id>
# </貢獻者>
# <貢獻者>
# <id type="integer">4404</id>
# <name>David Heinemeier Hansson</name>
# <rank type="integer">2</rank>
# <url-id>david-heinemeier-hansson</url-id>
# </貢獻者>
# </貢獻者>
為此,它依次將 to_xml
傳送到每個專案,並在根節點下收集結果。所有專案都必須回應 to_xml
,否則會引發異常。
預設情況下,根元素的名稱是第一項的類名稱的下劃線和破折號複數形式,前提是其餘元素屬於該型別(使用 is_a?
檢查)並且它們不是雜湊。在上面的例子中是“貢獻者”。
如果有任何元素不屬於第一個元素的型別,則根節點將成為“物件”:
[Contributor.first, Commit.first].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
# <物件>
# <id type="integer">4583</id>
# <name>Aaron Batalion</name>
# <rank type="integer">53</rank>
# <url-id>aaron-batalion</url-id>
# </物件>
# <物件>
# <author>Joshua Peek</author>
# <authored-timestamp type="datetime">2009-09-02T16:44:36Z</authored-timestamp>
# <branch>起源/主</branch>
# <committed-timestamp type="datetime">2009-09-02T16:44:36Z</committed-timestamp>
# <committer>Joshua Peek</committer>
# <git-show nil="true"></git-show>
# <id type="integer">190316</id>
# <imported-from-svn type="boolean">false</imported-from-svn>
# <message>殺死 AMo 觀察 wrap_with_notifications 因為 ARes 只使用它</message>
# <sha1>723a47bfb3708f968821bc969a9a3fc873a3ed58</sha1>
# </物件>
# </物件>
如果接收者是雜湊陣列,則根元素預設也是“物件”:
[{a: 1, b: 2}, {c: 3}].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
# <物件>
# <b type="integer">2</b>
# <a type="integer">1</a>
# </物件>
# <物件>
# <c type="integer">3</c>
# </物件>
# </物件>
警告。如果集合為空,則根元素預設為“nil-classes”。這是一個問題,例如,如果集合為空,則上述貢獻者列表的根元素將不是“貢獻者”,而是“nil-classes”。您可以使用 :root
選項來確保一致的根元素。
預設情況下,子節點的名稱是單數化的根節點的名稱。在上面的例子中,我們已經看到了“貢獻者”和“物件”。選項 :children
允許您設定這些節點名稱。
預設的 XML 構建器是 Builder::XmlMarkup
的新實例。您可以透過 :builder
選項設定您自己的構建器。該方法還接受諸如 :dasherize
和朋友之類的選項,將它們轉發給構建器:
Contributor.limit(2).order(:rank).to_xml(skip_types: true)
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <貢獻者>
# <貢獻者>
# <id>4356</id>
# <name>傑瑞米·肯珀</name>
# <rank>1</rank>
# <url-id>jeremy-kemper</url-id>
# </貢獻者>
# <貢獻者>
# <id>4404</id>
# <name>David Heinemeier Hansson</name>
# <rank>2</rank>
# <url-id>david-heinemeier-hansson</url-id>
# </貢獻者>
# </貢獻者>
在 active_support/core_ext/array/conversions.rb
中定義。
11.5 包裝
方法 Array.wrap
將其引數包裝在一個數組中,除非它已經是一個數組(或類似陣列)。
具體來說:
- 如果引數是
nil
,則返回一個空陣列。 - 否則,如果引數回應
to_ary
則呼叫它,如果to_ary
的 value 不是nil
,則返回。 - 否則,返回一個以引數為單個元素的陣列。
Array.wrap(nil) # => []
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(0) # => [0]
該方法的目的與 Kernel#Array
類似,但有一些差別:
- 如果引數回應
to_ary
,則呼叫該方法。如果返回的 value 是nil
,則Kernel#Array
繼續嘗試to_a
,但Array.wrap
立即返回一個以引數作為其單個元素的陣列。 - 如果從
to_ary
返回的 value 既不是nil
也不是Array
物件,則Kernel#Array
會引發異常,而Array.wrap
不會,它只會返回 value。 - 它不會在引數上呼叫
to_a
,如果引數不回應to_ary
,它將返回一個以引數為單個元素的陣列。
最後一點特別值得對一些可列舉進行比較:
Array.wrap(foo: :bar) # => [{:foo=>:bar}]
Array(foo: :bar) # => [[:foo, :bar]]
還有一個使用 splat 運算子的相關成語:
[*object]
在 active_support/core_ext/array/wrap.rb
中定義。
11.6 複製
方法 Array#deep_dup
複製自身和裡面的所有物件
遞迴地使用 Active Support 方法 Object#deep_dup
。它的工作原理類似於 Array#map
,向內部的每個物件傳送 deep_dup
方法。
array = [1, [2, 3]]
dup = array.deep_dup
dup[1][2] = 4
array[1][2] == nil # => true
在 active_support/core_ext/object/deep_dup.rb
中定義。
11.7 分組
11.7.1 in_groups_of(number, fill_with = nil)
方法 in_groups_of
將陣列拆分為特定大小的連續組。它返回一個包含組的陣列:
[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]
或者如果傳遞了一個塊,則依次產生它們:
<% sample.in_groups_of(3) do |a, b, c| %>
<tr>
<td><%= a %></td>
<td><%= b %></td>
<td><%= c %></td>
</tr>
<% end %>
第一個示例顯示 in_groups_of
如何根據需要使用盡可能多的 nil
元素填充最後一組以具有請求的大小。您可以使用第二個可選引數更改此填充 value:
[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]
您可以透過傳遞 false
來告訴該方法不要填充最後一組:
[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]
因此,false
不能用作填充 value。
在 active_support/core_ext/array/grouping.rb
中定義。
11.7.2 in_groups(number, fill_with = nil)
方法 in_groups
將陣列拆分為一定數量的組。該方法返回一個包含組的陣列:
%w(1 2 3 4 5 6 7).in_groups(3)
# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]
或者如果傳遞了一個塊,則依次產生它們:
%w(1 2 3 4 5 6 7).in_groups(3) {|group| p group}
["1", "2", "3"]
["4", "5", nil]
["6", "7", nil]
上面的示例顯示 in_groups
根據需要使用尾隨的 nil
元素填充某些組。一個組最多可以獲得這些額外元素中的一個,如果有的話,最右邊的一個。擁有它們的組總是最後一個。
您可以使用第二個可選引數更改此填充 value:
%w(1 2 3 4 5 6 7).in_groups(3, "0")
# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]
您可以透過傳遞 false
來告訴該方法不要填充較小的組:
%w(1 2 3 4 5 6 7).in_groups(3, false)
# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]
因此,false
不能用作填充 value。
在 active_support/core_ext/array/grouping.rb
中定義。
11.7.3 split(value = nil)
方法 split
將陣列除以分隔符並返回結果塊。
如果傳遞了一個塊,則分隔符是該塊為其返回 true 的陣列元素:
(-5..5).to_a.split { |i| i.multiple_of?(4) }
# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]
否則,作為引數接收的 value,預設為 nil
,是分隔符:
[0, 1, -5, 1, 1, "foo", "bar"].split(1)
# => [[0], [-5], [], ["foo", "bar"]]
提示:在前面的示例中觀察到,連續的分隔符會導致空陣列。
在 active_support/core_ext/array/grouping.rb
中定義。
12 Hash
的擴充套件
12.1 轉換
12.1.1 to_xml
方法 to_xml
返回一個包含其接收者的 XML 表示的字串:
{"foo" => 1, "bar" => 2}.to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <雜湊>
# <foo type="integer">1</foo>
# <bar type="integer">2</bar>
# </雜湊>
為此,該方法遍歷這些對並構建依賴於 values 的節點。給定一對 key
, value
:
如果
value
是一個雜湊,則遞迴呼叫key
作為:root
。如果
value
是一個數組,則遞迴呼叫key
為:root
,key
單數化為:children
。如果
value
是一個可呼叫物件,它必須期待一兩個引數。根據 arity,呼叫可呼叫物件時,將options
雜湊作為第一個引數,將key
作為:root
,將key
作為第二個引數進行單數化。它的返回 value 成為一個新節點。如果
value
回應to_xml
,則呼叫該方法時將key
作為:root
。否則,建立一個帶有
key
作為標籤的節點,用字串表示的value
作為文字節點。如果value
是nil
,則新增屬性“nil”設定為“true”。除非選項:skip_types
存在且為真,否則還會根據以下對映新增屬性“type”:
XML_TYPE_NAMES = {
"Symbol" => "symbol",
"Integer" => "integer",
"BigDecimal" => "decimal",
"Float" => "float",
"TrueClass" => "boolean",
"FalseClass" => "boolean",
"Date" => "date",
"DateTime" => "datetime",
"Time" => "datetime"
}
預設情況下,根節點是“hash”,但可以透過 :root
選項進行設定。
預設的 XML 構建器是 Builder::XmlMarkup
的新實例。您可以使用 :builder
選項設定您自己的構建器。該方法還接受諸如 :dasherize
和朋友之類的選項,將它們轉發給構建器。
在 active_support/core_ext/hash/conversions.rb
中定義。
12.2 合併
Ruby 有一個內建方法 Hash#merge
合併兩個雜湊:
{a: 1, b: 1}.merge(a: 0, c: 2)
# => {:a=>0, :b=>1, :c=>2}
Active Support 定義了更多可能方便的合併雜湊的方法。
12.2.1 reverse_merge
和 reverse_merge!
在衝突的情況下,引數雜湊中的 key 在 merge
中獲勝。您可以使用以下習慣用法以緊湊的方式支援具有預設 values 的選項雜湊:
options = {length: 30, omission: "..."}.merge(options)
Active Support 定義了 reverse_merge
,以防您更喜歡這種替代符號:
options = options.reverse_merge(length: 30, omission: "...")
還有一個 bang 版本 reverse_merge!
執行合併:
options.reverse_merge!(length: 30, omission: "...")
警告。考慮到 reverse_merge!
可能會改變呼叫者的雜湊值,這可能是也可能不是一個好主意。
在 active_support/core_ext/hash/reverse_merge.rb
中定義。
12.2.2 reverse_update
方法 reverse_update
是 reverse_merge!
的別名,如上所述。
警告。請注意,reverse_update
沒有爆炸。
在 active_support/core_ext/hash/reverse_merge.rb
中定義。
12.2.3 deep_merge
和 deep_merge!
正如您在前面的示例中所看到的,如果在兩個雜湊中都找到了 key,則引數中的一個 value 獲勝。
Active Support 定義了 Hash#deep_merge
。在深度合併中,如果在兩個雜湊中都找到了 key 並且它們的 values 依次是雜湊,則它們的 merge 成為結果雜湊中的值:
{a: {b: 1}}.deep_merge(a: {c: 2})
# => {:a=>{:b=>1, :c=>2}}
方法 deep_merge!
執行深度合併。
在 active_support/core_ext/hash/deep_merge.rb
中定義。
12.3 深度複製
方法 Hash#deep_dup
複製自身和所有 keys 和 values
內部遞迴使用 Active Support 方法 Object#deep_dup
。它的工作原理類似於 Enumerator#each_with_object
,將 deep_dup
方法傳送到內部的每一對。
hash = { a: 1, b: { c: 2, d: [3, 4] } }
dup = hash.deep_dup
dup[:b][:e] = 5
dup[:b][:d] << 5
hash[:b][:e] == nil # => true
hash[:b][:d] == [3, 4] # => true
在 active_support/core_ext/object/deep_dup.rb
中定義。
12.4 使用 Keys
12.4.1 except
和 except!
方法 [except
][Hash#except] 返回一個雜湊,其中移除了引數列表中的 keys(如果存在):
{a: 1, b: 2}.except(:a) # => {:b=>2}
如果接收者回應 convert_key
,則在每個引數上呼叫該方法。這使得 except
可以很好地處理具有無差異訪問的雜湊,例如:
{a: 1}.with_indifferent_access.except(:a) # => {}
{a: 1}.with_indifferent_access.except("a") # => {}
還有 bang 變體 except!
將 keys 刪除到位。
在 active_support/core_ext/hash/except.rb
中定義。
12.4.2 stringify_keys
和 stringify_keys!
方法 stringify_keys
返回一個雜湊,該雜湊在接收器中具有 keys 的字串化版本。它透過向他們傳送 to_s
來實現:
{nil => nil, 1 => 1, a: :a}.stringify_keys
# => {"" => nil, "1" => 1, "a" => :a}
在 key 衝突的情況下,value 將是最近插入雜湊的一個:
{"a" => 1, a: 2}.stringify_keys
# 結果將是
# => {"a"=>2}
此方法可能很有用,例如可以輕鬆接受 symbols 和字串作為選項。例如 ActionView::Helpers::FormHelper
定義:
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
options = options.stringify_keys
options["type"] = "checkbox"
# ...
end
第二行可以安全地訪問“型別”key,並讓使用者傳遞:type
或“型別”。
還有 bang 變體 stringify_keys!
將 keys 字串化到位。
除此之外,可以使用 deep_stringify_keys
和 deep_stringify_keys!
將給定雜湊中的所有 keys 以及巢狀在其中的所有雜湊字串化。結果的一個例子是:
{nil => nil, 1 => 1, nested: {a: 3, 5 => 5}}.deep_stringify_keys
# => {""=>nil, "1"=>1, "nested"=>{"a"=>3, "5"=>5}}
在 active_support/core_ext/hash/keys.rb
中定義。
12.4.3 symbolize_keys
和 symbolize_keys!
在可能的情況下,方法 symbolize_keys
返回一個雜湊,該雜湊在接收器中具有 keys 的 symbolized 版本。它透過向他們傳送 to_sym
來實現:
{nil => nil, 1 => 1, "a" => "a"}.symbolize_keys
# => {nil=>nil, 1=>1, :a=>"a"}
警告。請注意,在前面的示例中,只有一個 key 被 symbol 化。
在 key 衝突的情況下,value 將是最近插入雜湊的一個:
{"a" => 1, a: 2}.symbolize_keys
# => {:a=>2}
此方法可能很有用,例如可以輕鬆接受 symbols 和字串作為選項。例如 ActionText::TagHelper
定義
def rich_text_area_tag(name, value = nil, options = {})
options = options.symbolize_keys
options[:input] ||= "trix_input_#{ActionText::TagHelper.id += 1}"
# ...
end
第三行可以安全地訪問 :input
key,讓使用者透過 :input
或“輸入”。
還有 bang 變體 [symbolize_keys!
][Hash#symbolize_keys!] symbolizes keys 到位。
除此之外,還可以使用 deep_symbolize_keys
和 [deep_symbolize_keys!
][Hash#deep_symbolize_keys!] 將所有給定的雜湊值和 ZHTW_230_WTHZ 中的所有給定雜湊值 ZHTZW_236_WHTZ 中的所有巢狀雜湊化。結果的一個例子是:
{nil => nil, 1 => 1, "nested" => {"a" => 3, 5 => 5}}.deep_symbolize_keys
# => {nil=>nil, 1=>1, 巢狀:{a:3, 5=>5}}
在 active_support/core_ext/hash/keys.rb
中定義。
12.4.4 to_options
和 to_options!
方法 to_options
和 to_options!
分別是 symbolize_keys
和 symbolize_keys!
的別名。
在 active_support/core_ext/hash/keys.rb
中定義。
12.4.5 assert_valid_keys
方法 assert_valid_keys
接收任意數量的引數,並檢查接收者是否有該列表之外的任何鍵。如果是,則 ArgumentError
被提升。
{a: 1}.assert_valid_keys(:a) # passes
{a: 1}.assert_valid_keys("a") # ArgumentError
例如,在構建 associations 時,Active Record 不接受未知選項。它透過 assert_valid_keys
實現該控制。
在 active_support/core_ext/hash/keys.rb
中定義。
12.5 使用 Values
12.5.1 deep_transform_values
和 deep_transform_values!
方法 deep_transform_values
返回一個新的雜湊,其中包含所有由區塊操作轉換的 values。這包括來自根雜湊和所有巢狀雜湊和陣列的 values。
hash = { person: { name: 'Rob', age: '28' } }
hash.deep_transform_values{ |value| value.to_s.upcase }
# => {person: {name: "ROB", age: "28"}}
還有 bang 變體 deep_transform_values!
使用塊操作破壞性地轉換所有 values。
在 active_support/core_ext/hash/deep_transform_values.rb
中定義。
12.6 切片
方法 slice!
僅用給定的 keys 替換雜湊,並返回包含刪除的鍵/value 對的雜湊。
hash = {a: 1, b: 2}
rest = hash.slice!(:a) # => {:b=>2}
hash # => {:a=>1}
在 active_support/core_ext/hash/slice.rb
中定義。
12.7 提取
方法 extract!
刪除並返回匹配給定 keys 的鍵/value 對。
hash = {a: 1, b: 2}
rest = hash.extract!(:a) # => {:a=>1}
hash # => {:b=>2}
extract!
方法返回與接收者相同的 Hash 子類。
hash = {a: 1, b: 2}.with_indifferent_access
rest = hash.extract!(:a).class
# => ActiveSupport::HashWithIndifferentAccess
在 active_support/core_ext/hash/slice.rb
中定義。
12.8 無差別訪問
方法 with_indifferent_access
從其接收器返回 ActiveSupport::HashWithIndifferentAccess
:
{a: 1}.with_indifferent_access["a"] # => 1
在 active_support/core_ext/hash/indifferent_access.rb
中定義。
13 Regexp
的擴充套件
13.1 multiline?
方法 multiline?
表示正則表示式是否設定了 /m
標誌,即點是否與換行符匹配。
%r{.}.multiline? # => false
%r{.}m.multiline? # => true
Regexp.new('.').multiline? # => false
Regexp.new('.', Regexp::MULTILINE).multiline? # => true
Rails 在一個地方使用這個方法,也在路由程式碼中。路由要求不允許使用多行正則表示式,並且此標誌可以簡化執行該約束。
def verify_regexp_requirements(requirements)
# ...
if requirement.multiline?
raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
end
# ...
end
在 active_support/core_ext/regexp.rb
中定義。
14 Range
的擴充套件
14.1 to_s
Active Support 擴充套件了 Range#to_s
方法,以便它理解可選的格式引數。在撰寫本文時,唯一支援的非預設格式是 :db
:
(Date.today..Date.tomorrow).to_s
# => "2009-10-25..2009-10-26"
(Date.today..Date.tomorrow).to_s(:db)
# => "在'2009-10-25'和'2009-10-26'之間"
如示例所示,:db
格式生成一個 BETWEEN
SQL 子句。 Active Record 使用它來支援條件中的範圍 values。
在 active_support/core_ext/range/conversions.rb
中定義。
14.2 ===
和 include?
方法 Range#===
和 Range#include?
說明某些 value 是否落在給定實例的兩端之間:
(2..3).include?(Math::E) # => true
Active Support 擴充套件了這些方法,以便引數可以依次為另一個範圍。在這種情況下,我們測試引數範圍的末端是否屬於接收器本身:
(1..10) === (3..7) # => true
(1..10) === (0..7) # => false
(1..10) === (3..11) # => false
(1...9) === (3..9) # => false
(1..10).include?(3..7) # => true
(1..10).include?(0..7) # => false
(1..10).include?(3..11) # => false
(1...9).include?(3..9) # => false
在 active_support/core_ext/range/compare_range.rb
中定義。
14.3 overlaps?
方法 Range#overlaps?
表示任何兩個給定的範圍是否有非空交集:
(1..10).overlaps?(7..11) # => true
(1..10).overlaps?(0..7) # => true
(1..10).overlaps?(11..27) # => false
在 active_support/core_ext/range/overlaps.rb
中定義。
15 Date
的擴充套件
15.1 計算
資訊:以下計算方法在 1582 年 10 月存在邊緣情況,因為 5..14 天不存在。為簡潔起見,本指南並未記錄他們當時的行為,但足以說明他們的行為符合您的預期。即,Date.new(1582, 10, 4).tomorrow
返回 Date.new(1582, 10, 15)
,依此類推。請檢查 Active Support 測試套件中的 test/core_ext/date_ext_test.rb
以瞭解預期行為。
15.1.1 Date.current
Active Support 將 Date.current
定義為當前時區中的今天。這與 Date.today
類似,不同之處在於它尊重使用者時區(如果已定義)。它還定義了 [Date.yesterday
][Date.yesterday] 和 Date.tomorrow
,以及實例謂詞 past?
、today?
, tomorrow?
, next_day?
, yesterday?
, [prev_day?
][DateAndTime::day_day?] ?]、Active Support、on_weekday?
和 on_weekend?
,所有這些都相對於 ZHTZW_0_
使用尊重使用者時區的方法進行日期比較時,請確保使用 Date.current
而不是 Date.today
。在某些情況下,使用者時區可能在未來與系統時區相比,Date.today
預設使用系統時區。這意味著 Date.today
可能等於 Date.yesterday
。
在 active_support/core_ext/date/calculations.rb
中定義。
15.1.2 命名日期
15.1.2.1 beginning_of_week
, end_of_week
方法 beginning_of_week
和 end_of_week
返回日期
分別是一週的開始和結束。假定周開始於
星期一,但可以透過傳遞引數來更改,將執行緒設定為本地
Date.beginning_of_week
或 config.beginning_of_week
。
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.beginning_of_week # => Mon, 03 May 2010
d.beginning_of_week(:sunday) # => Sun, 02 May 2010
d.end_of_week # => Sun, 09 May 2010
d.end_of_week(:sunday) # => Sat, 08 May 2010
beginning_of_week
別名為 at_beginning_of_week
,end_of_week
別名為 at_end_of_week
。
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
15.1.2.2 monday
, sunday
方法 monday
和 sunday
返回前一個星期一的日期和
下週日,分別。
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.monday # => Mon, 03 May 2010
d.sunday # => Sun, 09 May 2010
d = Date.new(2012, 9, 10) # => Mon, 10 Sep 2012
d.monday # => Mon, 10 Sep 2012
d = Date.new(2012, 9, 16) # => Sun, 16 Sep 2012
d.sunday # => Sun, 16 Sep 2012
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
15.1.2.3 prev_week
, next_week
方法 next_week
接收一個帶有英文日期名稱的 symbol(預設是執行緒本地 Date.beginning_of_week
,或 config.beginning_of_week
,或 :monday
返回對應的日期)那天。
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.next_week # => Mon, 10 May 2010
d.next_week(:saturday) # => Sat, 15 May 2010
方法 prev_week
是類似的:
d.prev_week # => Mon, 26 Apr 2010
d.prev_week(:saturday) # => Sat, 01 May 2010
d.prev_week(:friday) # => Fri, 30 Apr 2010
prev_week
別名為 last_week
。
當設定了 Date.beginning_of_week
或 config.beginning_of_week
時,next_week
和 prev_week
都按預期工作。
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
15.1.2.4 beginning_of_month
, end_of_month
方法 beginning_of_month
和 end_of_month
返回月份的開始和結束日期:
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_month # => Sat, 01 May 2010
d.end_of_month # => Mon, 31 May 2010
beginning_of_month
別名為 at_beginning_of_month
,end_of_month
別名為 at_end_of_month
。
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
15.1.2.5 beginning_of_quarter
, end_of_quarter
方法 beginning_of_quarter
和 end_of_quarter
返回接收方日曆年季度開始和結束的日期:
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_quarter # => Thu, 01 Apr 2010
d.end_of_quarter # => Wed, 30 Jun 2010
beginning_of_quarter
的別名為 at_beginning_of_quarter
,而 end_of_quarter
的別名為 at_end_of_quarter
。
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
15.1.2.6 beginning_of_year
, end_of_year
方法 beginning_of_year
和 end_of_year
返回年份的開始和結束日期:
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_year # => Fri, 01 Jan 2010
d.end_of_year # => Fri, 31 Dec 2010
beginning_of_year
別名為 at_beginning_of_year
,end_of_year
別名為 at_end_of_year
。
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
15.1.3 其他日期計算
15.1.3.1 years_ago
, years_since
方法 years_ago
接收若干年並返回與多年前相同的日期:
date = Date.new(2010, 6, 7)
date.years_ago(10) # => Wed, 07 Jun 2000
years_since
時間向前移動:
date = Date.new(2010, 6, 7)
date.years_since(10) # => Sun, 07 Jun 2020
如果這樣的一天不存在,則返回相應月份的最後一天:
Date.new(2012, 2, 29).years_ago(3) # => Sat, 28 Feb 2009
Date.new(2012, 2, 29).years_since(3) # => Sat, 28 Feb 2015
last_year
是 #years_ago(1)
的簡寫。
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
15.1.3.2 months_ago
, months_since
方法 months_ago
和 months_since
類似地工作幾個月:
Date.new(2010, 4, 30).months_ago(2) # => Sun, 28 Feb 2010
Date.new(2010, 4, 30).months_since(2) # => Wed, 30 Jun 2010
如果這樣的一天不存在,則返回相應月份的最後一天:
Date.new(2010, 4, 30).months_ago(2) # => Sun, 28 Feb 2010
Date.new(2009, 12, 31).months_since(2) # => Sun, 28 Feb 2010
last_month
是 #months_ago(1)
的簡寫。
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
15.1.3.3 weeks_ago
方法 weeks_ago
類似地工作數週:
Date.new(2010, 5, 24).weeks_ago(1) # => Mon, 17 May 2010
Date.new(2010, 5, 24).weeks_ago(2) # => Mon, 10 May 2010
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
15.1.3.4 advance
跳轉到其他日期的最通用方法是 [advance
][Date#advance]。此方法接收帶有 keys :years
、:months
、:weeks
、:days
的雜湊值,並返回與當前 keys 指示一樣多的日期:
date = Date.new(2010, 6, 6)
date.advance(years: 1, weeks: 2) # => Mon, 20 Jun 2011
date.advance(months: 2, days: -2) # => Wed, 04 Aug 2010
請注意,在前面的示例中,增量可能為負。
為了執行計算,該方法首先遞增年,然後是月,然後是周,最後是天。這個命令在幾個月結束時很重要。比如說,我們現在是 2010 年 2 月底,我們想向前推進一個月零一天。
advance
方法先提前一個月,然後一天,結果是:
Date.new(2010, 2, 28).advance(months: 1, days: 1)
# => 星期日,2010 年 3 月 29 日
而如果反過來,結果會有所不同:
Date.new(2010, 2, 28).advance(days: 1).advance(months: 1)
# => 星期四,2010 年 4 月 1 日
在 active_support/core_ext/date/calculations.rb
中定義。
15.1.4 更改元件
方法 [change
][Date#change] 允許您獲取與接收者相同的新日期,除了給定的年、月或日:
Date.new(2010, 12, 23).change(year: 2011, month: 11)
# => 2011 年 11 月 23 日,星期三
此方法不容忍不存在的日期,如果更改無效 ArgumentError
引發:
Date.new(2010, 1, 31).change(month: 2)
# => ArgumentError: 無效日期
在 active_support/core_ext/date/calculations.rb
中定義。
15.1.5 持續時間
Duration
物件可以新增到日期和從日期中減去:
d = Date.current
# => 2010 年 8 月 9 日星期一
d + 1.year
# => 2011 年 8 月 9 日,星期二
d - 3.hours
# => 星期日,2010 年 8 月 8 日 21:00:00 UTC +00:00
它們轉換為對 since
或 advance
的呼叫。例如在這裡我們得到了日曆改革的正確跳轉:
Date.new(1582, 10, 4) + 1.day
# => 1582 年 10 月 15 日,星期五
15.1.6 時間戳
資訊:如果可能,以下方法返回 Time
物件,否則返回 DateTime
。如果設定,則它們遵循使用者時區。
15.1.6.1 beginning_of_day
, end_of_day
方法 [beginning_of_day
][Date#beginning_of_day] 返回一天開始時的時間戳 (00:00:00):
date = Date.new(2010, 6, 7)
date.beginning_of_day # => Mon Jun 07 00:00:00 +0200 2010
方法 [end_of_day
][Date#end_of_day] 返回一天結束時的時間戳(23:59:59):
date = Date.new(2010, 6, 7)
date.end_of_day # => Mon Jun 07 23:59:59 +0200 2010
beginning_of_day
別名為 [at_beginning_of_day
][Date#at_beginning_of_day]、[midnight
][Date#midnight]、[at_midnight
][Date#at_midnight]。
在 active_support/core_ext/date/calculations.rb
中定義。
15.1.6.2 beginning_of_hour
, end_of_hour
方法 beginning_of_hour
返回小時開始時的時間戳 (hh:00:00):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_hour # => Mon Jun 07 19:00:00 +0200 2010
方法 end_of_hour
返回小時結束時的時間戳 (hh:59:59):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010
beginning_of_hour
別名為 at_beginning_of_hour
。
在 active_support/core_ext/date_time/calculations.rb
中定義。
15.1.6.3 beginning_of_minute
, end_of_minute
方法 beginning_of_minute
返回分鐘開始時的時間戳 (hh:mm:00):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_minute # => Mon Jun 07 19:55:00 +0200 2010
方法 end_of_minute
返回分鐘結束時的時間戳 (hh:mm:59):
date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_minute # => Mon Jun 07 19:55:59 +0200 2010
beginning_of_minute
別名為 at_beginning_of_minute
。
資訊:beginning_of_hour
、end_of_hour
、beginning_of_minute
和 end_of_minute
是為 Time
和 DateTime
實現的,但不是 Date
,因為在 ZHTZW_6 上請求小時或分鐘的開始或結束沒有意義。
在 active_support/core_ext/date_time/calculations.rb
中定義。
15.1.6.4 ago
, since
方法 [ago
][Date#ago] 接收秒數作為引數,並從午夜返回幾秒前的時間戳:
date = Date.current # => Fri, 11 Jun 2010
date.ago(1) # => Thu, 10 Jun 2010 23:59:59 EDT -04:00
同樣, [since
][Date#since] 向前移動:
date = Date.current # => Fri, 11 Jun 2010
date.since(1) # => Fri, 11 Jun 2010 00:00:01 EDT -04:00
在 active_support/core_ext/date/calculations.rb
中定義。
15.1.7 其他時間計算
15.2 轉換
16 DateTime
的擴充套件
DateTime
不知道 DST 規則,因此當 DST 更改正在進行時,其中一些方法具有邊緣情況。例如 seconds_since_midnight
可能不會返回這樣一天的實際金額。
16.1 計算
類 DateTime
是 Date
的子類,因此透過載入 active_support/core_ext/date/calculations.rb
,您可以繼承這些方法及其別名,只是它們將始終返回日期時間。
重新實現了以下方法,因此您不需要為這些方法載入 active_support/core_ext/date/calculations.rb
:
- [
beginning_of_day
][DateTime#beginning_of_day] / [midnight
][DateTime#midnight] /at_midnight
/at_beginning_of_day
end_of_day
- [
ago
][DateTime#ago] - [
since
][DateTime#since] / [in
][DateTime#in]
另一方面,advance
和 [change
][DateTime#change] 也被定義並支援更多選項,它們在下面記錄。
以下方法僅在 active_support/core_ext/date_time/calculations.rb
中實現,因為它們僅在與 DateTime
實例一起使用時才有意義:
16.1.1 命名日期時間
16.1.1.1 DateTime.current
Active Support 將 DateTime.current
定義為類似於 Time.now.to_datetime
,除了它遵循使用者時區(如果已定義)。實例謂詞 past?
和 future?
是相對於 DateTime.current
定義的。
在 active_support/core_ext/date_time/calculations.rb
中定義。
16.1.2 其他擴充套件
16.1.2.1 seconds_since_midnight
方法 seconds_since_midnight
返回自午夜以來的秒數:
now = DateTime.current # => Mon, 07 Jun 2010 20:26:36 +0000
now.seconds_since_midnight # => 73596
在 active_support/core_ext/date_time/calculations.rb
中定義。
16.1.2.2 utc
方法 [utc
][DateTime#utc] 在接收器中為您提供以 UTC 表示的相同日期時間。
now = DateTime.current # => Mon, 07 Jun 2010 19:27:52 -0400
now.utc # => Mon, 07 Jun 2010 23:27:52 +0000
此方法也別名為 getutc
。
在 active_support/core_ext/date_time/calculations.rb
中定義。
16.1.2.3 utc?
謂詞 utc?
表示接收方是否將 UTC 作為其時區:
now = DateTime.now # => Mon, 07 Jun 2010 19:30:47 -0400
now.utc? # => false
now.utc.utc? # => true
在 active_support/core_ext/date_time/calculations.rb
中定義。
16.1.2.4 advance
跳轉到另一個日期時間的最通用方法是 advance
。此方法接收帶有 keys :years
、:months
、:weeks
、:days
、:hours
、:minutes
和 :seconds
的雜湊值,並返回與 ZHTW_3 一樣多的當前日期時間。
d = DateTime.current
# => 2010 年 8 月 5 日星期四 11:33:31 +0000
d.advance(years: 1, months: 1, days: 1, hours: 1, minutes: 1, seconds: 1)
# => 2011 年 9 月 6 日,星期二 12:34:32 +0000
此方法首先計算目標日期,將 :years
、:months
、:weeks
和 :days
傳遞到上面記錄的 Date#advance
。之後,它將呼叫 [since
][DateTime#since] 的時間調整為提前的秒數。這個順序是相關的,在某些邊緣情況下,不同的順序會給出不同的日期時間。 Date#advance
中的示例適用,我們可以擴充套件它以顯示與時間位相關的順序相關性。
如果我們首先移動日期位(也有一個相對的處理順序,如前所述),然後我們得到例如以下計算的時間位:
d = DateTime.new(2010, 2, 28, 23, 59, 59)
# => 星期日,2010 年 2 月 28 日 23:59:59 +0000
d.advance(months: 1, seconds: 1)
# => 2010 年 3 月 29 日星期一 00:00:00 +0000
但是如果我們反過來計算它們,結果就會不同:
d.advance(seconds: 1).advance(months: 1)
# => 2010 年 4 月 1 日星期四 00:00:00 +0000
由於 DateTime
不是 DST 感知的,因此您可能會在一個不存在的時間點結束,而沒有警告或錯誤告訴您。
在 active_support/core_ext/date_time/calculations.rb
中定義。
16.1.3 更改元件
方法 [change
][DateTime#change] 允許您獲得與接收者相同的新日期時間,除了給定的選項,可能包括 :year
、:month
、:day
、:hour
、:min
、ZTHWZHT_8TH6
now = DateTime.current
# => 2010 年 6 月 8 日,星期二 01:56:22 +0000
now.change(year: 2011, offset: Rational(-6, 24))
# => 2011 年 6 月 8 日,星期三 01:56:22 -0600
如果小時歸零,那麼分鐘和秒也歸零(除非他們給出了 values):
now.change(hour: 0)
# => 2010 年 6 月 8 日,星期二 00:00:00 +0000
類似地,如果分鐘歸零,那麼秒也歸零(除非它給出了 value):
now.change(min: 0)
# => 2010 年 6 月 8 日,星期二 01:00:00 +0000
此方法不容忍不存在的日期,如果更改無效 ArgumentError
引發:
DateTime.current.change(month: 2, day: 30)
# => ArgumentError: 無效日期
在 active_support/core_ext/date_time/calculations.rb
中定義。
16.1.4 持續時間
Duration
物件可以新增到日期時間和從中減去:
now = DateTime.current
# => 2010 年 8 月 9 日星期一 23:15:17 +0000
now + 1.year
# => 2011 年 8 月 9 日,星期二 23:15:17 +0000
now - 1.week
# => 2010 年 8 月 2 日星期一 23:15:17 +0000
它們轉換為對 since
或 advance
的呼叫。例如在這裡我們得到了日曆改革的正確跳轉:
DateTime.new(1582, 10, 4, 23) + 1.hour
# => 1582 年 10 月 15 日星期五 00:00:00 +0000
17 Time
的擴充套件
17.1 計算
它們是類似的。請參閱上面的文件並考慮以下差異:
- [
change
][Time#change] 接受額外的:usec
選項。 -
Time
瞭解 DST,因此您可以獲得正確的 DST 計算,如
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
# 在巴塞羅那,由於夏令時,2010/03/28 02:00 +0100 變為 2010/03/28 03:00 +0200。
t = Time.local(2010, 3, 28, 1, 59, 59)
# => 2010 年 3 月 28 日星期日 01:59:59 +0100
t.advance(seconds: 1)
# => 2010 年 3 月 28 日星期日 03:00:00 +0200
- 如果 [
since
][Time#since] 或 [ago
][Time#ago] 跳轉到無法用Time
表示的時間,則返回DateTime
物件。
17.1.1 Time.current
Active Support 將 Time.current
定義為當前時區中的今天。這與 Time.now
類似,不同之處在於它尊重使用者時區(如果已定義)。它還定義了實例謂詞 past?
、today?
、tomorrow?
、next_day?
::Calculations#next_day?]、yesterday?
、prev_day?
和 future?
,所有這些相對於 Time.current
。
使用尊重使用者時區的方法進行時間比較時,請確保使用 Time.current
而不是 Time.now
。在某些情況下,使用者時區可能在未來與系統時區相比,Time.now
預設使用系統時區。這意味著 Time.now.to_date
可能等於 Date.yesterday
。
在 active_support/core_ext/time/calculations.rb
中定義。
17.1.2 all_day
, all_week
, all_month
, all_quarter
和 all_year
方法 all_day
返回一個表示當前時間一整天的範圍。
now = Time.current
# => 2010 年 8 月 9 日星期一 23:20:05 UTC +00:00
now.all_day
# => 2010 年 8 月 9 日星期一 00:00:00 UTC +00:00.. 2010 年 8 月 9 日星期一 23:59:59 UTC +00:00
類似地,all_week
、all_month
、all_quarter
和 [all_year
][Date#AllTime::year]Calculations服務於生成時間範圍的目的。
now = Time.current
# => 2010 年 8 月 9 日星期一 23:20:05 UTC +00:00
now.all_week
# => 2010 年 8 月 9 日星期一 00:00:00 UTC +00:00.. 2010 年 8 月 15 日星期日 23:59:59 UTC +00:00
now.all_week(:sunday)
# => 2012 年 9 月 16 日星期日 00:00:00 UTC +00:00.. 2012 年 9 月 22 日星期六 23:59:59 UTC +00:00
now.all_month
# => 2010 年 8 月 1 日星期六 00:00:00 UTC +00:00.. 2010 年 8 月 31 日星期二 23:59:59 UTC +00:00
now.all_quarter
# => 2010 年 7 月 1 日星期四 00:00:00 UTC +00:00..星期四 2010 年 9 月 30 日 23:59:59 UTC +00:00
now.all_year
# => 2010 年 1 月 1 日星期五 00:00:00 UTC +00:00..星期五 2010 年 12 月 31 日 23:59:59 UTC +00:00
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
17.1.3 prev_day
, next_day
[prev_day
][Time#prev_day] 和 [next_day
][Time#next_day] 返回最後一天或第二天的時間:
t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_day # => 2010-05-07 00:00:00 +0900
t.next_day # => 2010-05-09 00:00:00 +0900
在 active_support/core_ext/time/calculations.rb
中定義。
17.1.4 prev_month
, next_month
[prev_month
][Time#prev_month] 和 [next_month
][Time#next_month] 返回上個月或下個月同一天的時間:
t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_month # => 2010-04-08 00:00:00 +0900
t.next_month # => 2010-06-08 00:00:00 +0900
如果這樣的一天不存在,則返回相應月份的最後一天:
Time.new(2000, 5, 31).prev_month # => 2000-04-30 00:00:00 +0900
Time.new(2000, 3, 31).prev_month # => 2000-02-29 00:00:00 +0900
Time.new(2000, 5, 31).next_month # => 2000-06-30 00:00:00 +0900
Time.new(2000, 1, 31).next_month # => 2000-02-29 00:00:00 +0900
在 active_support/core_ext/time/calculations.rb
中定義。
17.1.5 prev_year
, next_year
[prev_year
][Time#prev_year] 和 [next_year
][Time#next_year] 返回上一年或下一年同一天/月的時間:
t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_year # => 2009-05-08 00:00:00 +0900
t.next_year # => 2011-05-08 00:00:00 +0900
如果日期是閏年的 2 月 29 日,您將獲得 28 日:
t = Time.new(2000, 2, 29) # => 2000-02-29 00:00:00 +0900
t.prev_year # => 1999-02-28 00:00:00 +0900
t.next_year # => 2001-02-28 00:00:00 +0900
在 active_support/core_ext/time/calculations.rb
中定義。
17.1.6 prev_quarter
, next_quarter
prev_quarter
和 next_quarter
返回上一季度或下一季度同一天的日期:
t = Time.local(2010, 5, 8) # => 2010-05-08 00:00:00 +0300
t.prev_quarter # => 2010-02-08 00:00:00 +0200
t.next_quarter # => 2010-08-08 00:00:00 +0300
如果這樣的一天不存在,則返回相應月份的最後一天:
Time.local(2000, 7, 31).prev_quarter # => 2000-04-30 00:00:00 +0300
Time.local(2000, 5, 31).prev_quarter # => 2000-02-29 00:00:00 +0200
Time.local(2000, 10, 31).prev_quarter # => 2000-07-31 00:00:00 +0300
Time.local(2000, 11, 31).next_quarter # => 2001-03-01 00:00:00 +0200
prev_quarter
別名為 last_quarter
。
在 active_support/core_ext/date_and_time/calculations.rb
中定義。
17.2 時間建構函式
如果定義了使用者時區,則 Active Support 將 Time.current
定義為 Time.zone.now
,並回退到 Time.now
:
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
Time.current
# => 2010 年 8 月 6 日星期五 17:11:58 CEST +02:00
類似於 DateTime
,謂詞 past?
和 future?
與 Time.current
相關。
如果構建的時間超出了執行時平臺中Time
支援的範圍,則丟棄usecs並返回一個DateTime
物件。
17.2.1 持續時間
Duration
物件可以新增和減去時間物件:
now = Time.current
# => 2010 年 8 月 9 日星期一 23:20:05 UTC +00:00
now + 1.year
# => 2011 年 8 月 9 日星期二 23:21:11 UTC +00:00
now - 1.week
# => 2010 年 8 月 2 日星期一 23:21:11 UTC +00:00
它們轉換為對 since
或 advance
的呼叫。例如在這裡我們得到了日曆改革的正確跳轉:
Time.utc(1582, 10, 3) + 5.days
# => 10 月 18 日星期一 00:00:00 UTC 1582
18 File
的擴充套件
18.1 atomic_write
使用類方法 File.atomic_write
,您可以以防止任何讀者看到半寫內容的方式寫入檔案。
檔名作為引數傳遞,該方法產生一個為寫入而開啟的檔案控制代碼。一旦塊完成 atomic_write
關閉檔案控制代碼並完成其工作。
比如Action Pack就是用這個方法來寫像all.css
這樣的資產快取檔案:
File.atomic_write(joined_asset_path) do |cache|
cache.write(join_asset_file_contents(asset_paths))
end
為了完成這個 atomic_write
建立一個臨時檔案。這是塊中的程式碼實際寫入的檔案。完成後,臨時檔案被重新命名,這是 POSIX 系統上的原子操作。如果目標檔案存在 atomic_write
將覆蓋它並保留所有者和許可權。但是,在少數情況下 atomic_write
無法更改檔案所有權或許可權,此錯誤會被捕獲並跳過信任使用者/檔案系統以確保需要它的程序可以訪問該檔案。
筆記。由於 atomic_write
執行的 chmod 操作,如果目標檔案上設定了 ACL,則將重新計算/修改此 ACL。
警告。請注意,您不能附加 atomic_write
。
輔助檔案寫入臨時檔案的標準目錄中,但您可以傳遞您選擇的目錄作為第二個引數。
在 active_support/core_ext/file/atomic.rb
中定義。
19 NameError
的擴充套件
Active Support 將 missing_name?
新增到 NameError
,它測試是否由於作為引數傳遞的名稱而引發異常。
該名稱可以作為 symbol 或字串給出。 symbol 針對裸常量名稱進行測試,字串針對完全限定的常量名稱進行測試。
提示:符號可以表示完全限定的常量名稱,如 :"ActiveRecord::Base"
,因此定義 symbols 的行為是為了方便,而不是因為技術上必須如此。
例如,當呼叫 ArticlesController
的 action 時,Rails 會樂觀地嘗試使用 ArticlesHelper
。 helper module 不存在是可以的,因此如果引發該常量名稱的異常,則應將其靜音。但可能是由於實際未知常數,articles_helper.rb
會引發 NameError
。那應該重新加註。 missing_name?
方法提供了一種區分兩種情況的方法:
def default_helper_module!
module_name = name.delete_suffix("Controller")
module_path = module_name.underscore
helper module_path
rescue LoadError => e
raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
在 active_support/core_ext/name_error.rb
中定義。
20 LoadError
的擴充套件
Active Support 將 is_missing?
新增到 LoadError
。
給定路徑名 is_missing?
測試是否由於該特定檔案引發異常(“.rb”副檔名除外)。
例如,當呼叫 ArticlesController
的 action 時,Rails 嘗試載入 articles_helper.rb
,但該檔案可能不存在。沒關係,helper module 不是強制性的,因此 Rails 消除了載入錯誤。但也可能是 helper module 確實存在,進而需要另一個缺失的庫。在這種情況下,Rails 必須重新引發異常。 is_missing?
方法提供了一種區分兩種情況的方法:
def default_helper_module!
module_name = name.delete_suffix("Controller")
module_path = module_name.underscore
helper module_path
rescue LoadError => e
raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
在 active_support/core_ext/load_error.rb
中定義。
回饋
我們鼓勵您幫助提高本指南的品質。
如果您發現任何拼寫錯誤或資訊錯誤,請提供回饋。 要開始回饋,您可以閱讀我們的 回饋 部分。
您還可能會發現不完整的內容或不是最新的內容。 請務必為 main 新增任何遺漏的文件。假設是 非穩定版指南(edge guides) 請先驗證問題是否已經在主分支上解決。 請前往 Ruby on Rails 指南寫作準則 查看寫作風格和慣例。
如果由於某種原因您發現要修復的內容但無法自行修補,請您 提出 issue。
關於 Ruby on Rails 的任何類型的討論歡迎提供任何文件至 rubyonrails-docs 討論區。