1 コア拡張機能のロード方法
1.1 スタンドアロンのActive Support
Active Supportは、デフォルトで可能な限り最小の依存関係をロードするために、最小限の依存関係をロードします。それは小さな部分に分割されているため、必要な拡張機能のみをロードすることができます。また、関連する拡張機能を一度にロードするための便利なエントリーポイントもあります。
したがって、次のような単純なrequire
の後には、Active Supportフレームワークで必要な拡張機能のみがロードされます。
require "active_support"
1.1.1 定義の選択
この例では、Hash#with_indifferent_access
をロードする方法を示しています。この拡張機能は、Hash
をActiveSupport::HashWithIndifferentAccess
に変換し、キーを文字列またはシンボルとしてアクセスできるようにします。
{ 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
へのすべての拡張機能をロードすることです。SomeClass
への拡張機能は、active_support/core_ext/some_class
をロードすることで一度に利用できるという規則です。
したがって、すべての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 Ruby on Railsアプリケーション内のActive Support
Ruby on Railsアプリケーションは、config.active_support.bare
がtrueでない限り、すべてのActive Supportをロードします。その場合、アプリケーションはフレームワーク自体が必要とするものだけを選択してロードし、前のセクションで説明したように、任意の粒度で自身を選択することもできます。
2 すべてのオブジェクトへの拡張機能
2.1 blank?
とpresent?
Railsアプリケーションでは、次の値が空と見なされます:
nil
とfalse
、空白のみで構成された文字列(以下の注意事項を参照)、
空の配列とハッシュ、および
empty?
に応答し、空である他のオブジェクト。
文字列の述語は、Unicode対応の文字クラス[:space:]
を使用しているため、たとえばU+2029(段落セパレータ)は空白と見なされます。
数字は言及されていません。特に、0と0.0は空ではありません。
たとえば、このActionController::HttpAuthentication::Token::ControllerMethods
のメソッドでは、トークンが存在するかどうかを確認するためにblank?
を使用しています。
def authenticate(controller, &login_procedure)
token, options = token_and_options(controller.request)
unless token.blank?
login_procedure.call(token, options)
end
end
メソッド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
presence
メソッドは、present?
であればレシーバー自体を返し、そうでなければnil
を返します。次のようなイディオムに便利です:
ruby
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
メソッドは、指定されたオブジェクトのディープコピーを返します。通常、他のオブジェクトを含むオブジェクトを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
を使用することです。try
はnil
に送信された場合にnil
を返す点を除いて、Object#public_send
と似ています。
以下に例を示します。
# tryを使用しない場合
unless @number.nil?
@number.next
end
# tryを使用する場合
@number.try(:next)
もう1つの例は、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!
を使用してください。
@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
を使用して、任意のオブジェクトのシングルトンクラスのコンテキストでコードを評価することができます。
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?
メソッドは、単純な規則に基づいて、あるクラスが別のクラスのように振る舞うかどうかをチェックする方法を提供します。
ruby
def acts_like_string?
end
これは単なるマーカーであり、その本体や返り値は関係ありません。その後、クライアントコードは次のようにしてダックタイプの安全性をクエリできます。
some_klass.acts_like?(:string)
Railsには、Date
やTime
のように振る舞い、この契約に従うクラスがあります。
注意:active_support/core_ext/object/acts_like.rb
で定義されています。
2.8 to_param
Railsのすべてのオブジェクトは、to_param
メソッドに応答します。このメソッドは、クエリ文字列やURLフラグメントとしてオブジェクトを表すものを返すためのものです。
デフォルトでは、to_param
は単にto_s
を呼び出します。
7.to_param # => "7"
to_param
の返り値はエスケープされてはいけません。
"Tom & Jerry".to_param # => "Tom & Jerry"
Railsのいくつかのクラスはこのメソッドを上書きしています。
例えば、nil
、true
、false
はそれ自体を返します。Array#to_param
は要素にto_param
を呼び出し、結果を"/"で結合します。
[0, true, String].to_param # => "0/true/String"
特に、Railsのルーティングシステムはモデルのto_param
を呼び出して:id
プレースホルダーの値を取得します。ActiveRecord::Base#to_param
はモデルのid
を返しますが、モデルでこのメソッドを再定義することもできます。例えば、次のように定義されている場合、
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
次のようになります。
user_path(@user) # => "/users/357-john-smith"
警告:コントローラはto_param
の再定義に注意する必要があります。なぜなら、"357-john-smith"のようなリクエストが来た場合、params[:id]
の値がそれになるからです。
注意:active_support/core_ext/object/to_param.rb
で定義されています。
2.9 to_query
to_query
メソッドは、指定されたkey
をto_param
の返り値に関連付けるクエリ文字列を構築します。例えば、次のto_param
の定義がある場合、
class User
def to_param
"#{id}-#{name.parameterize}"
end
end
次のようになります。
current_user.to_query('user') # => "user=357-john-smith"
このメソッドは、必要なものをエスケープします。キーと値の両方に対してです。
account.to_query('company[name]')
# => "company%5Bname%5D=Johnson+%26+Johnson"
そのため、出力はクエリ文字列で使用する準備ができています。
配列は、各要素に対してkey[]
をキーとしてto_query
を適用し、結果を"&"で結合します。
[3.4, -45.6].to_query('sample')
# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"
ハッシュもto_query
に応答しますが、異なるシグネチャを持ちます。引数が渡されない場合、呼び出しは値にto_query(key)
を呼び出してソートされたキー/値の割り当てのシリーズを生成します。それから結果を"&"で結合します。
{ c: 3, b: 2, a: 1 }.to_query # => "a=1&b=2&c=3"
メソッドHash#to_query
は、キーに対するオプションの名前空間を受け入れます。
{ 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
はブロックに対してプロキシオブジェクトをyieldします。ブロック内では、プロキシに対して呼び出されたメソッドは、オプションがマージされた状態でレシーバに転送されます。例えば、次のようにして重複を取り除くことができます。
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
ジェムが提供する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
メソッドは、"@"を含まないインスタンス変数名を対応する値にマッピングするハッシュを返します。キーは文字列です。
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
メソッドとenable_warnings
メソッドは、ブロックの実行中に$VERBOSE
の値を適切に変更し、その後にリセットします。
silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }
suppress
メソッドを使用すると、例外を抑制することも可能です。このメソッドは任意の数の例外クラスを受け取ります。ブロックの実行中に例外が発生し、引数のいずれかにkind_of?
である場合、suppress
は例外をキャプチャして無視します。それ以外の場合、例外はキャプチャされません。
# ユーザーがロックされている場合、インクリメントは失われますが、大したことではありません。
suppress(ActiveRecord::StaleObjectError) do
current_user.increment! :visits
end
注意:active_support/core_ext/kernel/reporting.rb
で定義されています。
2.14 in?
述語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
モデルの属性には、リーダー、ライター、および述語があります。alias_attribute
を使用すると、対応する3つのメソッドがすべて定義されたモデル属性のエイリアスを作成できます。他のエイリアスメソッドと同様に、新しい名前は最初の引数で、古い名前は2番目の引数です(代入を行う場合と同じ順序になることを覚えておくと覚えやすいです)。
class User < ApplicationRecord
# emailカラムを"login"として参照できます。
# 認証コードに意味があるかもしれません。
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
というマクロを定義しています。これらは、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
がどこかにあるsprintf
のような形式の文字列を渡すことができます。そこに名前が配置されます。デフォルトは"@_%s"
です。
Railsでは、ビューなどのいくつかの場所で内部属性を使用しています。
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 モジュール属性
mattr_reader
、mattr_writer
、およびmattr_accessor
というマクロは、クラスのために定義されたcattr_*
マクロと同じです。実際、cattr_*
マクロはmattr_*
マクロのエイリアスです。クラス属性を参照してください。
例えば、Active Storageのロガーの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_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"
トップレベルまたは無名のモジュールの場合、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 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
ただし、無名のモジュールは定義により到達不能です。
注:active_support/core_ext/module/anonymous.rb
で定義されています。
3.4 メソッドの委譲
3.4.1 delegate
マクロdelegate
は、メソッドを簡単に転送する方法を提供します。
あるアプリケーションのユーザーがUser
モデルにログイン情報を持っているが、名前やその他のデータは別のProfile
モデルにあると想像してください。
class User < ApplicationRecord
has_one :profile
end
この設定では、ユーザーの名前はプロファイルを介して取得できますが、便利な場合にはその属性に直接アクセスできると便利です。
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
オプションはメソッドが委譲されるオブジェクトに評価される式になるべきです。通常は文字列またはシンボルです。その式はレシーバーのコンテキストで評価されます。
# 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
前の例では、street
ではなくaddress_street
が生成されます。
警告:この場合、生成されるメソッドの名前は対象オブジェクトと対象メソッドの名前で構成されているため、:to
オプションはメソッド名である必要があります。
カスタムの接頭辞も設定できます:
delegate :size, to: :attachment, prefix: :avatar
前の例では、マクロはsize
ではなくavatar_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
は、階層のどのレベルでもオーバーライドできる1つ以上の継承可能なクラス属性を宣言します。
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, Aから来ます
a2.x # => 2, a2でオーバーライドされました
オプション:instance_writer
をfalse
に設定すると、ライターインスタンスメソッドの生成を防ぐことができます。
module ActiveRecord
class Base
class_attribute :table_name_prefix, instance_writer: false, default: "my"
end
end
モデルは、その属性を設定するための方法として、そのオプションを有用とする場合があります。
オプション: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
を返します。
インスタンス述語を使用しない場合は、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
は、クラスのattr_*
と同様ですが、クラスに対して使用します。存在しない場合、クラス変数をnil
で初期化し、それにアクセスするための対応するクラスメソッドを生成します。
class MysqlAdapter < AbstractAdapter
# @@emulate_booleansにアクセスするためのクラスメソッドを生成します。
cattr_accessor :emulate_booleans
end
また、cattr_*
には、デフォルト値を使用して属性を設定するためのブロックを渡すこともできます。
class MysqlAdapter < AbstractAdapter
# デフォルト値がtrueの@@emulate_booleansにアクセスするためのクラスメソッドを生成します。
cattr_accessor :emulate_booleans, default: true
end
便利のために、インスタンスメソッドも作成されますが、それらはクラス属性へのプロキシです。したがって、インスタンスはクラス属性を変更することができますが、class_attribute
(上記参照)のようにオーバーライドすることはできません。例えば、以下のように与えられた場合、
module ActionView
class Base
cattr_accessor :field_error_proc, default: Proc.new { ... }
end
end
ビューでfield_error_proc
にアクセスすることができます。
リーダーインスタンスメソッドの生成は、:instance_reader
をfalse
に設定することで防止することができます。また、ライターインスタンスメソッドの生成は、:instance_writer
をfalse
に設定することで防止することができます。両方のメソッドの生成を防止するには、:instance_accessor
をfalse
に設定します。いずれの場合も、値はfalse
でなければなりません。
module A
class B
# first_nameのインスタンスリーダーは生成されません。
cattr_accessor :first_name, instance_reader: false
# last_name=のインスタンスライターは生成されません。
cattr_accessor :last_name, instance_writer: false
# surnameのインスタンスリーダーまたはsurname=のライターは生成されません。
cattr_accessor :surname, instance_accessor: false
end
end
モデルは、属性の設定を防ぐために、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ページに挿入することはできません。なぜなら、レビュータイトルが「Flanagan & Matz rules!」の場合、出力は正しく形成されないからです。なぜなら、アンパサンドは「&」としてエスケープする必要があるからです。さらに、アプリケーションによっては、ユーザーが手作りのレビュータイトルを設定して悪意のあるHTMLを注入できるため、これは大きなセキュリティホールになる可能性があります。リスクについては、セキュリティガイドのクロスサイトスクリプティングのセクションを参照してください。
5.1.2 安全な文字列
Active Supportには、(html) safe文字列の概念があります。安全な文字列は、そのままHTMLに挿入できるとマークされています。エスケープされているかどうかに関係なく、信頼されています。
デフォルトでは、文字列は安全ではないと見なされます。
"".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 # => "<"
これらのメソッドは通常のビューでは使用しないでください。安全でない値は自動的にエスケープされます。
<%= @review.title %> <%# 必要に応じてエスケープされます %>
verbatimを挿入する場合は、html_safe
を呼び出す代わりにraw
ヘルパーを使用します。
<%= raw @cms.current_template %> <%# @cms.current_templateをそのまま挿入する %>
または、<%==
を使用することもできます。
<%== @cms.current_template %> <%# @cms.current_templateをそのまま挿入する %>
raw
ヘルパーは、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)
# => "Oh dear! Oh dear!..."
省略記号は:omission
オプションでカスタマイズできます。
"Oh dear! Oh dear! I shall be late!".truncate(20, omission: '…')
# => "Oh dear! Oh …"
特に、切り詰めは省略文字列の長さも考慮します。
自然な区切りで文字列を切り詰めるには、:separator
を渡します。
"Oh dear! Oh dear! I shall be late!".truncate(18)
# => "Oh dear! Oh dea..."
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: ' ')
# => "Oh dear! Oh..."
オプションの:separator
は正規表現にすることもできます。
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: /\s/)
# => "Oh dear! Oh..."
上記の例では、最初に"dear"が切り詰められますが、その後に: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)
# => "Oh dear! Oh dear!..."
省略記号は:omission
オプションでカスタマイズできます。
"Oh dear! Oh dear! I shall be late!".truncate_words(4, omission: '…')
# => "Oh dear! Oh dear!…"
自然な区切りで文字列を切り詰めるには、:separator
を渡します。
"Oh dear! Oh dear! I shall be late!".truncate_words(3, separator: '!')
# => "Oh dear! Oh dear! I shall be late..."
オプションの:separator
は正規表現にすることもできます。
"Oh dear! Oh dear! I shall be late!".truncate_words(4, separator: /\s/)
# => "Oh dear! Oh dear!..."
注意: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?
の3人称のエイリアスを定義しています。
"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
は、ヒアドキュメント内のインデントを削除します。
例えば、
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
2番目の引数である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
は通常、スペースまたはタブのいずれかですが、任意の文字列にすることもできます。
3番目の引数であるindent_empty_lines
は、空行をインデントするかどうかを示すフラグです。デフォルトはfalseです。
"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
文字を含む部分文字列を返します。
str.first(n)
の呼び出しは、n
> 0の場合はstr.to(n-1)
と同等であり、n
== 0の場合は空の文字列を返します。
注意:active_support/core_ext/string/access.rb
で定義されています。
5.11.5 last(limit = 1)
last
メソッドは、文字列の最後のlimit
文字を含む部分文字列を返します。
str.last(n)
の呼び出しは、n
> 0の場合は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
の値が1以外の場合、複数形が返されます。
"dude".pluralize(0) # => "dudes"
"dude".pluralize(1) # => "dude"
"dude".pluralize(2) # => "dudes"
Active Recordは、このメソッドを使用してモデルに対応するデフォルトのテーブル名を計算します。
# 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"
関連付けは、このメソッドを使用して対応するデフォルトの関連クラスの名前を計算します。
# 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のクラスやモジュール名に変換するメソッドと考えることができます。スラッシュは名前空間を区切ります。
"backoffice/session".camelize # => "Backoffice::Session"
たとえば、Action Packは特定のセッションストアを提供するクラスをロードするためにこのメソッドを使用します。
# 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
を使用します。
# actionpack/lib/abstract_controller/base.rb
def controller_path
@controller_path ||= name.delete_suffix("Controller").underscore
end
たとえば、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"
モデルの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
引数をオーバーライドします。
ruby
"Employee Salary".downcase_first # => "employee Salary"
"".downcase_first # => ""
Defined in active_support/core_ext/string/inflections.rb
.
Defined in active_support/core_ext/numeric/conversions.rb
.
注意:active_support/core_ext/numeric/conversions.rb
で定義されています。
6 Integer
への拡張
6.1 multiple_of?
multiple_of?
メソッドは、整数が引数の倍数であるかどうかをテストします。
2.multiple_of?(1) # => true
1.multiple_of?(2) # => false
注意:active_support/core_ext/integer/multiple.rb
で定義されています。
6.2 ordinal
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
で定義されています。
6.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
で定義されています。
6.4 Time
以下のメソッド:
は、4.months + 5.years
のような時間の宣言と計算を可能にします。これらの戻り値は、Timeオブジェクトに加算または減算することもできます。
これらのメソッドは、正確な日付の計算のためにfrom_now
、ago
などと組み合わせることができます。例えば:
# Time.current.advance(months: 1)と同等
1.month.from_now
# Time.current.advance(years: 2)と同等
2.years.from_now
# Time.current.advance(months: 4, years: 5)と同等
(4.months + 5.years).from_now
警告:他の期間については、Numeric
への時間の拡張を参照してください。
注意:active_support/core_ext/integer/time.rb
で定義されています。
7 BigDecimal
への拡張
7.1 to_s
to_s
メソッドは、デフォルトの指定子として「F」を提供します。これにより、to_s
の単純な呼び出しは、工学表記ではなく浮動小数点表現になります。
BigDecimal(5.00, 6).to_s # => "5.0"
工学表記もサポートされています。
BigDecimal(5.00, 6).to_s("e") # => "0.5E1"
8 Enumerable
への拡張
8.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
はコレクションの要素をイテレータとして使用し、返された値を合計します。
(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
で定義されています。
8.2 index_by
index_by
メソッドは、列挙可能な要素をキーとするハッシュを生成します。
コレクションを反復処理し、各要素をブロックに渡します。要素は、ブロックが返す値によってキー付けされます。
invoices.index_by(&:number)
# => {'2009-032' => <Invoice ...>, '2009-008' => <Invoice ...>, ...}
警告:通常、キーは一意である必要があります。ブロックが異なる要素に対して同じ値を返す場合、そのキーに対してはコレクションが構築されません。最後のアイテムが優先されます。
注意:active_support/core_ext/enumerable.rb
で定義されています。
8.3 index_with
index_with
メソッドは、列挙可能な要素をキーとするハッシュを生成します。値は、渡されたデフォルト値またはブロックで返されます。
post = Post.new(title: "hey there", body: "what's up?")
%i( title body ).index_with { |attr_name| post.public_send(attr_name) }
# => { title: "hey there", body: "what's up?" }
WEEKDAYS.index_with(Interval.all_day)
# => { monday: [ 0, 1440 ], … }
注意:active_support/core_ext/enumerable.rb
で定義されています。
8.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
で定義されています。
8.5 exclude?
述語exclude?
は、指定されたオブジェクトがコレクションに含まれていないかどうかをテストします。これは組み込みのinclude?
の否定です。
to_visit << node if visited.exclude?(node)
注意:active_support/core_ext/enumerable.rb
で定義されています。
8.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
で定義されています。
8.7 excluding
メソッドexcluding
は、指定された要素を除いた列挙可能オブジェクトのコピーを返します。
["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
excluding
はwithout
のエイリアスです。
注意:active_support/core_ext/enumerable.rb
で定義されています。
8.8 pluck
メソッドpluck
は、各要素から指定されたキーを抽出します。
[{ 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
で定義されています。
8.9 pick
メソッドpick
は、最初の要素から指定されたキーを抽出します。
[{ 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
で定義されています。
9 Array
への拡張
9.1 アクセス
Active Supportは、配列のAPIを拡張して、特定のアクセス方法を容易にします。例えば、to
は、指定されたインデックスまでの要素のサブ配列を返します。
%w(a b c d).to(2) # => ["a", "b", "c"]
[].to(7) # => []
同様に、from
は、指定されたインデックスから末尾までの要素を返します。インデックスが配列の長さよりも大きい場合、空の配列が返されます。
%w(a b c d).from(2) # => ["c", "d"]
%w(a b c d).from(10) # => []
[].from(0) # => []
メソッドincluding
は、渡された要素を含む新しい配列を返します。
[ 1, 2, 3 ].including(4, 5) # => [ 1, 2, 3, 4, 5 ]
[ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]
メソッドexcluding
は、指定された要素を除いた配列のコピーを返します。これは、パフォーマンスのためにArray#reject
の代わりにArray#-
を使用するEnumerable#excluding
の最適化です。
["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
[ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ]) # => [ [ 0, 1 ] ]
メソッドsecond
、third
、fourth
、fifth
は、対応する要素を返します。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
で定義されています。
9.2 抽出
メソッドextract!
は、ブロックがtrueを返す要素を削除して返します。ブロックが指定されていない場合、代わりにEnumeratorが返されます。
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
で定義されています。
9.3 オプションの抽出
メソッド呼び出しの最後の引数がハッシュである場合、&block
引数を除いて括弧を省略することができます。
User.exists?(email: params[:email])
このような構文糖衣は、Railsでは位置引数が多すぎる場合に位置引数を避けるためによく使用され、代わりに名前付きパラメータをエミュレートするインターフェースを提供します。特に、オプションのために末尾のハッシュを使用することは非常にイディオマチックです。
しかし、メソッドが可変長の引数を受け取り、その宣言で*
を使用している場合、このようなオプションのハッシュは引数の配列の要素として扱われ、役割を失います。
そのような場合、extract_options!
を使用してオプションのハッシュを特別な扱いにすることができます。このメソッドは配列の最後の要素の型をチェックします。ハッシュであればそれを取り出して返し、そうでなければ空のハッシュを返します。
例えば、caches_action
コントローラーマクロの定義を見てみましょう。
def caches_action(*actions)
return unless cache_configured?
options = actions.extract_options!
# ...
end
このメソッドは任意の数のアクション名とオプションのハッシュを最後の引数として受け取ります。extract_options!
の呼び出しにより、オプションのハッシュを取得し、actions
から削除することができます。
注意:active_support/core_ext/array/extract_options.rb
で定義されています。
9.4 変換
9.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"
このメソッドは3つのオプションを受け入れます。
:two_words_connector
: 長さ2の配列に使用されるものです。デフォルトは " and " です。:words_connector
: 3つ以上の要素を持つ配列の要素を結合するために使用されるものです。ただし、最後の2つを除きます。デフォルトは ", " です。:last_word_connector
: 3つ以上の要素を持つ配列の最後の要素を結合するために使用されるものです。デフォルトは ", and " です。
これらのオプションのデフォルト値はローカライズできます。キーは次のとおりです。
オプション | I18nキー |
---|---|
: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
で定義されています。
9.4.2 to_fs
to_fs
メソッドは、デフォルトではto_s
と同様の動作をします。
ただし、配列にid
に応答する要素が含まれている場合、引数としてシンボル:db
を渡すことができます。これは通常、Active Recordオブジェクトのコレクションで使用されます。返される文字列は次のようになります。
[].to_fs(:db) # => "null"
[user].to_fs(:db) # => "8456"
invoice.lines.to_fs(:db) # => "23,567,556,12"
上記の例の整数は、それぞれのid
への呼び出しから取得されるものとします。
注意:active_support/core_ext/array/conversions.rb
で定義されています。
9.4.3 to_xml
to_xml
メソッドは、受け取った配列のXML表現を含む文字列を返します。
Contributor.limit(2).order(:rank).to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors type="array">
# <contributor>
# <id type="integer">4356</id>
# <name>Jeremy Kemper</name>
# <rank type="integer">1</rank>
# <url-id>jeremy-kemper</url-id>
# </contributor>
# <contributor>
# <id type="integer">4404</id>
# <name>David Heinemeier Hansson</name>
# <rank type="integer">2</rank>
# <url-id>david-heinemeier-hansson</url-id>
# </contributor>
# </contributors>
これを行うために、それぞれのアイテムにto_xml
を送信し、結果をルートノードの下に収集します。すべてのアイテムはto_xml
に応答する必要があります。そうでない場合は例外が発生します。
デフォルトでは、ルート要素の名前は最初のアイテムのクラスのアンダースコアとダッシュを含む複数形になります。ただし、他の要素がそのタイプに属していること(is_a?
でチェックされる)とハッシュでないことが条件です。上記の例では、それは "contributors" です。
最初の要素のタイプと異なる要素がある場合、ルートノードは "objects" になります。 ```ruby [Contributor.first, Commit.first].to_xml
=>
<?xml version="1.0" encoding="UTF-8"?>
もしレシーバがハッシュの配列である場合、ルート要素はデフォルトで "objects" になります。
```ruby
[{ a: 1, b: 2 }, { c: 3 }].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
# <object>
# <b type="integer">2</b>
# <a type="integer">1</a>
# </object>
# <object>
# <c type="integer">3</c>
# </object>
# </objects>
注意: コレクションが空の場合、ルート要素はデフォルトで "nil-classes" になります。これは注意が必要です。例えば、上記の貢献者リストのルート要素は "contributors" ではなく、コレクションが空の場合は "nil-classes" になります。一貫したルート要素を確保するために、:root
オプションを使用することができます。
子ノードの名前はデフォルトでルートノードの名前の単数形になります。上記の例では "contributor" と "object" を見ました。:children
オプションを使用してこれらのノード名を設定することができます。
デフォルトの XML ビルダーは Builder::XmlMarkup
の新しいインスタンスです。:builder
オプションを使用して独自のビルダーを設定することができます。また、:dasherize
などのオプションも受け入れます。これらのオプションはビルダーに転送されます。
Contributor.limit(2).order(:rank).to_xml(skip_types: true)
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors>
# <contributor>
# <id>4356</id>
# <name>Jeremy Kemper</name>
# <rank>1</rank>
# <url-id>jeremy-kemper</url-id>
# </contributor>
# <contributor>
# <id>4404</id>
# <name>David Heinemeier Hansson</name>
# <rank>2</rank>
# <url-id>david-heinemeier-hansson</url-id>
# </contributor>
# </contributors>
注意: active_support/core_ext/array/conversions.rb
で定義されています。
9.5 ラッピング
Array.wrap
メソッドは、引数が既に配列(または配列のようなオブジェクト)でない場合、引数を配列でラップします。
具体的には:
- 引数が
nil
の場合、空の配列が返されます。 - それ以外の場合、引数が
to_ary
を呼び出すことができる場合、to_ary
が呼び出され、to_ary
の値がnil
でない場合はそれが返されます。 - それ以外の場合、引数を単一の要素として持つ配列が返されます。
Array.wrap(nil) # => []
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(0) # => [0]
このメソッドは Kernel#Array
の目的と似ていますが、いくつかの違いがあります:
- 引数が
to_ary
を呼び出すことができる場合、メソッドが呼び出されます。Kernel#Array
は返された値がnil
の場合にto_a
を試みますが、Array.wrap
は引数を単一の要素として持つ配列をすぐに返します。 to_ary
から返される値がnil
でもArray
オブジェクトでもない場合、Kernel#Array
は例外を発生させますが、Array.wrap
は例外を発生させずに値を返します。- 引数が
to_ary
に応答しない場合、to_a
を呼び出しません。その場合、引数を単一の要素として持つ配列が返されます。
特に、いくつかの列挙可能なオブジェクトについて比較する価値がある最後のポイントです:
Array.wrap(foo: :bar) # => [{:foo=>:bar}]
Array(foo: :bar) # => [[:foo, :bar]]
また、スプラット演算子を使用した関連するイディオムもあります:
[*object]
注意: active_support/core_ext/array/wrap.rb
で定義されています。
9.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
で定義されています。
9.7 グループ化
9.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
要素で埋めます。2番目のオプション引数を使用して、このパディング値を変更することができます。
[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
はパディング値として使用できません。
注意:active_support/core_ext/array/grouping.rb
で定義されています。
9.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
要素で埋めます。グループには最大で1つの追加要素が含まれることがありますが、それらを含むのは常に最後のグループです。
2番目のオプション引数を使用して、このパディング値を変更することができます。
%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
はパディング値として使用できません。
注意:active_support/core_ext/array/grouping.rb
で定義されています。
9.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]]
そうでない場合、デフォルトでnil
となる引数として受け取った値が区切り文字です。
[0, 1, -5, 1, 1, "foo", "bar"].split(1)
# => [[0], [-5], [], ["foo", "bar"]]
前の例では、連続する区切り文字は空の配列になります。
注意:active_support/core_ext/array/grouping.rb
で定義されています。
10 Hash
への拡張
10.1 変換
10.1.1 to_xml
メソッドto_xml
は、受け取ったハッシュのXML表現を含む文字列を返します。
{ foo: 1, bar: 2 }.to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <hash>
# <foo type="integer">1</foo>
# <bar type="integer">2</bar>
# </hash>
これを行うために、メソッドはペアをループし、値に応じてノードを構築します。ペアkey
、value
が与えられた場合:
value
がハッシュの場合、key
を:root
として再帰呼び出しを行います。value
が配列の場合、key
を:root
、key
の単数形を:children
として再帰呼び出しを行います。value
が呼び出し可能なオブジェクトの場合、1つまたは2つの引数を受け取る必要があります。引数の数に応じて、呼び出し可能オブジェクトはoptions
ハッシュを最初の引数として、key
を:root
、単数形のkey
を2番目の引数として呼び出されます。その戻り値は新しいノードになります。value
がto_xml
に応答する場合、メソッドはkey
を:root
として呼び出されます。それ以外の場合、
key
をタグとするノードが作成され、value
の文字列表現がテキストノードとして追加されます。value
がnil
の場合、属性"nil"が"true"に設定されます。オプション:skip_types
が存在し、trueである場合を除き、次のマッピングに従って属性"type"も追加されます。ruby 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
で定義されています。
10.2 マージ
Ruby には、2 つのハッシュをマージするための組み込みメソッド Hash#merge
があります。
{ a: 1, b: 1 }.merge(a: 0, c: 2)
# => {:a=>0, :b=>1, :c=>2}
Active Support では、便利なハッシュのマージ方法をいくつか定義しています。
10.2.1 reverse_merge
と reverse_merge!
merge
では、引数のハッシュのキーが衝突した場合、引数のハッシュのキーが優先されます。このイディオムを使用して、デフォルト値を持つオプションハッシュをコンパクトにサポートすることができます。
options = { length: 30, omission: "..." }.merge(options)
Active Support では、この代替記法として reverse_merge
を定義しています。
options = options.reverse_merge(length: 30, omission: "...")
また、マージをインプレースで実行するバンバージョン reverse_merge!
も定義されています。
options.reverse_merge!(length: 30, omission: "...")
注意:reverse_merge!
は呼び出し元のハッシュを変更する可能性があるため、良いアイデアであるかどうかは考慮してください。
注意:active_support/core_ext/hash/reverse_merge.rb
で定義されています。
10.2.2 reverse_update
reverse_update
メソッドは、上記で説明した reverse_merge!
のエイリアスです。
注意:reverse_update
にはバンがありません。
注意:active_support/core_ext/hash/reverse_merge.rb
で定義されています。
10.2.3 deep_merge
と deep_merge!
前の例でわかるように、両方のハッシュでキーが見つかった場合、引数のハッシュの値が優先されます。
Active Support では Hash#deep_merge
を定義しています。ディープマージでは、両方のハッシュでキーが見つかり、その値が再びハッシュである場合、そのマージが結果のハッシュの値になります。
{ a: { b: 1 } }.deep_merge(a: { c: 2 })
# => {:a=>{:b=>1, :c=>2}}
メソッド deep_merge!
はインプレースでディープマージを実行します。
注意:active_support/core_ext/hash/deep_merge.rb
で定義されています。
10.3 ディープコピー
Hash#deep_dup
メソッドは、自身とそのキーと値を再帰的に複製します。これは、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
で定義されています。
10.4 キーの操作
10.4.1 except
と except!
except
メソッドは、引数リストに含まれるキーを削除したハッシュを返します。
{ a: 1, b: 2 }.except(:a) # => {:b=>2}
レシーバが convert_key
に応答する場合、引数の各要素に対してそのメソッドが呼び出されます。これにより、except
は、例えば indifferent access を持つハッシュとうまく動作することができます。
{ a: 1 }.with_indifferent_access.except(:a) # => {}
{ a: 1 }.with_indifferent_access.except("a") # => {}
また、バンバージョンの except!
もあり、キーをインプレースで削除します。
注意:active_support/core_ext/hash/except.rb
で定義されています。
10.4.2 stringify_keys
と stringify_keys!
stringify_keys
メソッドは、レシーバのキーの文字列化バージョンを持つハッシュを返します。これは、各キーに対して to_s
を送信することで行われます。
{ nil => nil, 1 => 1, a: :a }.stringify_keys
# => {"" => nil, "1" => 1, "a" => :a}
キーの衝突がある場合、値はハッシュに最後に挿入された値になります。
{ "a" => 1, a: 2 }.stringify_keys
# 結果は
# => {"a"=>2}
このメソッドは、例えばシンボルと文字列の両方をオプションとして受け入れるために便利です。例えば、ActionView::Helpers::FormHelper
では次のように定義されています。
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
options = options.stringify_keys
options["type"] = "checkbox"
# ...
end
2行目では、安全に"type"キーにアクセスし、ユーザーが:type
または"type"のいずれかを渡すことができます。
また、stringify_keys!
というバンジョンもあり、キーをその場で文字列に変換します。
それ以外にも、与えられたハッシュとその中にネストされたすべてのハッシュのキーを文字列に変換するためにdeep_stringify_keys
とdeep_stringify_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
で定義されています。
10.4.3 symbolize_keys
とsymbolize_keys!
メソッドsymbolize_keys
は、受け取ったハッシュのキーのシンボル化バージョンを可能な限り返します。これは、それらにto_sym
を送信することによって行われます。
{ nil => nil, 1 => 1, "a" => "a" }.symbolize_keys
# => {nil=>nil, 1=>1, :a=>"a"}
警告。前の例では、キーが1つだけシンボル化されていることに注意してください。
キーの衝突の場合、値はハッシュに最後に挿入されたものになります。
{ "a" => 1, a: 2 }.symbolize_keys
# => {:a=>2}
このメソッドは、例えばシンボルと文字列の両方をオプションとして簡単に受け入れるために便利です。例えば、ActionText::TagHelper
では次のように定義されています。
def rich_text_area_tag(name, value = nil, options = {})
options = options.symbolize_keys
options[:input] ||= "trix_input_#{ActionText::TagHelper.id += 1}"
# ...
end
3行目では、安全に:input
キーにアクセスし、ユーザーが:input
または"input"のいずれかを渡すことができます。
また、symbolize_keys!
というバンジョンもあり、キーをその場でシンボルに変換します。
それ以外にも、与えられたハッシュとその中にネストされたすべてのハッシュのキーをシンボルに変換するためにdeep_symbolize_keys
とdeep_symbolize_keys!
を使用することができます。結果の例は次のとおりです。
{ nil => nil, 1 => 1, "nested" => { "a" => 3, 5 => 5 } }.deep_symbolize_keys
# => {nil=>nil, 1=>1, nested:{a:3, 5=>5}}
注意:active_support/core_ext/hash/keys.rb
で定義されています。
10.4.4 to_options
とto_options!
メソッドto_options
とto_options!
は、それぞれsymbolize_keys
とsymbolize_keys!
のエイリアスです。
注意:active_support/core_ext/hash/keys.rb
で定義されています。
10.4.5 assert_valid_keys
メソッドassert_valid_keys
は、任意の数の引数を受け取り、レシーバーにそのリスト外のキーがあるかどうかをチェックします。もしキーがあれば、ArgumentError
が発生します。
{ a: 1 }.assert_valid_keys(:a) # パスする
{ a: 1 }.assert_valid_keys("a") # ArgumentError
例えば、Active Recordでは、関連を構築する際に未知のオプションを受け入れません。これはassert_valid_keys
を使用して制御されています。
注意:active_support/core_ext/hash/keys.rb
で定義されています。
10.5 値の操作
10.5.1 deep_transform_values
とdeep_transform_values!
メソッドdeep_transform_values
は、ブロック操作によって変換されたすべての値を含む新しいハッシュを返します。これには、ルートハッシュとすべてのネストされたハッシュと配列の値が含まれます。
hash = { person: { name: 'Rob', age: '28' } }
hash.deep_transform_values { |value| value.to_s.upcase }
# => {person: {name: "ROB", age: "28"}}
また、ブロック操作を使用してすべての値を破壊的に変換するためのバンジョンdeep_transform_values!
もあります。
注意:active_support/core_ext/hash/deep_transform_values.rb
で定義されています。
10.6 スライス
メソッドslice!
は、指定されたキーのみを含むハッシュに置き換え、削除されたキー/値のペアを含むハッシュを返します。
hash = { a: 1, b: 2 }
rest = hash.slice!(:a) # => {:b=>2}
hash # => {:a=>1}
注意:active_support/core_ext/hash/slice.rb
で定義されています。
10.7 抽出
メソッドextract!
は、指定されたキーに一致するキー/値のペアを削除して返します。
hash = { a: 1, b: 2 }
rest = hash.extract!(:a) # => {:a=>1}
hash # => {:b=>2}
メソッドextract!
は、レシーバーと同じハッシュのサブクラスを返します。
```ruby
hash = { a: 1, b: 2 }.with_indifferent_access
rest = hash.extract!(:a).class
=> ActiveSupport::HashWithIndifferentAccess
注意:`active_support/core_ext/hash/slice.rb`で定義されています。
### 無差別アクセス
[`with_indifferent_access`][Hash#with_indifferent_access]メソッドは、レシーバーから[`ActiveSupport::HashWithIndifferentAccess`][ActiveSupport::HashWithIndifferentAccess]を返します。
```ruby
{ a: 1 }.with_indifferent_access["a"] # => 1
注意:active_support/core_ext/hash/indifferent_access.rb
で定義されています。
11 Regexp
の拡張
11.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
で定義されています。
12 Range
の拡張
12.1 to_fs
Active Supportは、オプションのフォーマット引数を理解するto_s
の代替としてRange#to_fs
を定義しています。現時点では、サポートされている非デフォルトのフォーマットは:db
のみです。
(Date.today..Date.tomorrow).to_fs
# => "2009-10-25..2009-10-26"
(Date.today..Date.tomorrow).to_fs(:db)
# => "BETWEEN '2009-10-25' AND '2009-10-26'"
例では、:db
フォーマットはBETWEEN
SQL句を生成します。これは、Active Recordが条件で範囲値をサポートするために使用されます。
注意:active_support/core_ext/range/conversions.rb
で定義されています。
12.2 ===
とinclude?
Range#===
メソッドとRange#include?
メソッドは、与えられた値がインスタンスの範囲の両端の間にあるかどうかを示します。
(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
で定義されています。
12.3 overlap?
Range#overlap?
メソッドは、2つの範囲が空でない交差部分を持つかどうかを示します。
(1..10).overlap?(7..11) # => true
(1..10).overlap?(0..7) # => true
(1..10).overlap?(11..27) # => false
注意:active_support/core_ext/range/overlap.rb
で定義されています。
13 Date
の拡張
13.1 計算
以下の計算メソッドには、1582年10月のエッジケースがあります。なぜなら、日付5〜14は存在しないからです。このガイドでは、簡潔さのためにこれらの日付の周りでの動作は文書化していませんが、期待される動作についてはActive Supportのテストスイートのtest/core_ext/date_ext_test.rb
を確認してください。
13.1.1 Date.current
Active Supportは、Date.current
を現在のタイムゾーンの今日の日付と定義しています。これはDate.today
と似ていますが、定義されている場合はユーザータイムゾーンを尊重します。また、Date.yesterday
とDate.tomorrow
、およびインスタンスの述語past?
、today?
、tomorrow?
、next_day?
、yesterday?
、prev_day?
、future?
、on_weekday?
、on_weekend?
も定義されています。これらはすべてDate.current
に対して相対的です。
ユーザータイムゾーンを尊重するメソッドを使用して日付の比較を行う場合は、Date.today
ではなくDate.current
を使用してください。ユーザータイムゾーンがシステムタイムゾーンよりも未来にある場合、Date.today
はDate.yesterday
と等しくなる可能性があるためです。
注意:active_support/core_ext/date/calculations.rb
で定義されています。
13.1.2 名前付き日付
13.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
で定義されています。
13.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
で定義されています。
13.1.2.3 prev_week
, next_week
next_week
メソッドは、英語の曜日名をシンボルで受け取ります(デフォルトはスレッドローカルの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
にエイリアスされています。
next_week
とprev_week
は、Date.beginning_of_week
またはconfig.beginning_of_week
が設定されている場合にも正常に動作します。
注意:active_support/core_ext/date_and_time/calculations.rb
で定義されています。
13.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
で定義されています。
13.1.2.5 quarter
, beginning_of_quarter
, end_of_quarter
quarter
メソッドは、レシーバのカレンダー年の四半期を返します。
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.quarter # => 2
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
で定義されています。
13.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
で定義されています。
13.1.3 その他の日付計算
13.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
で定義されています。
13.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
で定義されています。
13.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
で定義されています。
13.1.3.4 advance
他の日にジャンプする最も一般的な方法は、advance
です。このメソッドは、years
、months
、weeks
、days
というキーを持つハッシュを受け取り、現在のキーが示すだけ進んだ日付を返します。
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
前の例では、増分が負であることに注意してください。
注意:active_support/core_ext/date/calculations.rb
で定義されています。
13.1.4 コンポーネントの変更
change
メソッドを使用すると、指定した年、月、または日を除いて、受信者と同じ日付の新しい日付を取得できます。
Date.new(2010, 12, 23).change(year: 2011, month: 11)
# => Wed, 23 Nov 2011
このメソッドは存在しない日付に対しては許容されません。変更が無効な場合は、ArgumentError
が発生します。
Date.new(2010, 1, 31).change(month: 2)
# => ArgumentError: invalid date
注意:active_support/core_ext/date/calculations.rb
で定義されています。
13.1.5 持続時間
Duration
オブジェクトは、日付に追加したり、日付から減算したりすることができます。
d = Date.current
# => Mon, 09 Aug 2010
d + 1.year
# => Tue, 09 Aug 2011
d - 3.hours
# => Sun, 08 Aug 2010 21:00:00 UTC +00:00
これらはsince
またはadvance
への呼び出しに変換されます。たとえば、ここではカレンダー改革の正しいジャンプが得られます。
Date.new(1582, 10, 4) + 1.day
# => Fri, 15 Oct 1582
13.1.6 タイムスタンプ
以下のメソッドは、可能な場合はTime
オブジェクトを返し、それ以外の場合はDateTime
オブジェクトを返します。設定されている場合、ユーザーのタイムゾーンを尊重します。
13.1.6.1 beginning_of_day
、end_of_day
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
メソッドは、その日の終わり(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
、midnight
、at_midnight
としてエイリアスされています。
注意:active_support/core_ext/date/calculations.rb
で定義されています。
13.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
で定義されています。
13.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
としてエイリアスされています。
INFO:beginning_of_hour
、end_of_hour
、beginning_of_minute
、end_of_minute
は、Date
インスタンスでは時や分の始まりや終わりを要求する意味がないため、Time
とDateTime
に対して実装されていますが、Date
には実装されていません。
注意:active_support/core_ext/date_time/calculations.rb
で定義されています。
13.1.6.4 ago
、since
ago
メソッドは、指定した秒数前のタイムスタンプを返します。
date = Date.current # => Fri, 11 Jun 2010
date.ago(1) # => Thu, 10 Jun 2010 23:59:59 EDT -04:00
同様に、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
で定義されています。
14 DateTime
の拡張
警告:DateTime
はDSTのルールを認識していないため、一部のメソッドはDSTの変更が行われている場合にエッジケースが発生する可能性があります。たとえば、seconds_since_midnight
はそのような日には実際の値を返さないかもしれません。
14.1 計算
クラスDateTime
はDate
のサブクラスなので、active_support/core_ext/date/calculations.rb
をロードすることでこれらのメソッドとそのエイリアスを継承しますが、常に日時を返します。
以下のメソッドは再実装されているため、これらのメソッドについてはactive_support/core_ext/date/calculations.rb
をロードする必要はありません。
一方、advance
とchange
はより多くのオプションをサポートしており、以下で説明されています。
以下のメソッドはDateTime
インスタンスと一緒に使用する場合にのみ実装されています。
14.1.1 名前付き日時
14.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
で定義されています。
14.1.2 その他の拡張
14.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
で定義されています。
14.1.2.2 utc
メソッド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
で定義されています。
14.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
で定義されています。
14.1.2.4 advance
別の日時にジャンプする最も一般的な方法はadvance
です。このメソッドは、years
、months
、weeks
、days
、hours
、minutes
、seconds
というキーを持つハッシュを受け取り、それに応じて進められた日時を返します。
d = DateTime.current
# => Thu, 05 Aug 2010 11:33:31 +0000
d.advance(years: 1, months: 1, days: 1, hours: 1, minutes: 1, seconds: 1)
# => Tue, 06 Sep 2011 12:34:32 +0000
このメソッドはまず、Date#advance
でyears
、months
、weeks
、days
を渡して目的の日付を計算します。その後、since
を呼び出して進める秒数を指定して時間を調整します。この順序は重要であり、異なる順序では一部のエッジケースで異なる日時が得られます。Date#advance
の例が適用され、時間ビットに関連する順序の関連性を示すことができます。
たとえば、日付ビット(前述のように相対的な処理順序も持っています)を最初に移動し、その後に時間ビットを移動すると、次の計算結果が得られます。
d = DateTime.new(2010, 2, 28, 23, 59, 59)
# => Sun, 28 Feb 2010 23:59:59 +0000
d.advance(months: 1, seconds: 1)
# => Mon, 29 Mar 2010 00:00:00 +0000
しかし、逆の順序で計算すると、結果は異なります。
d.advance(seconds: 1).advance(months: 1)
# => Thu, 01 Apr 2010 00:00:00 +0000
警告:DateTime
はDSTを認識していないため、警告やエラーなしに存在しない時点に到達する可能性があります。
注意:active_support/core_ext/date_time/calculations.rb
で定義されています。
14.1.3 コンポーネントの変更
メソッドchange
を使用すると、与えられたオプション(year
、month
、day
、hour
、min
、sec
、offset
、start
)を除いて、受信者と同じ日時を持つ新しい日時を取得できます。
now = DateTime.current
# => Tue, 08 Jun 2010 01:56:22 +0000
now.change(year: 2011, offset: Rational(-6, 24))
# => Wed, 08 Jun 2011 01:56:22 -0600
時間がゼロになると、分と秒もゼロになります(値が指定されていない場合):
now.change(hour: 0)
# => Tue, 08 Jun 2010 00:00:00 +0000
同様に、分がゼロになると、秒もゼロになります(値が指定されていない場合):
now.change(min: 0)
# => Tue, 08 Jun 2010 01:00:00 +0000
このメソッドは存在しない日付に対しては許容されません。無効な変更がある場合は、ArgumentError
が発生します:
DateTime.current.change(month: 2, day: 30)
# => ArgumentError: invalid date
注意:active_support/core_ext/date_time/calculations.rb
で定義されています。
14.1.4 持続時間
Duration
オブジェクトは、日時に追加または減算できます:
now = DateTime.current
# => Mon, 09 Aug 2010 23:15:17 +0000
now + 1.year
# => Tue, 09 Aug 2011 23:15:17 +0000
now - 1.week
# => Mon, 02 Aug 2010 23:15:17 +0000
これらはsince
またはadvance
への呼び出しに変換されます。たとえば、ここではカレンダー改革の正しいジャンプが得られます:
DateTime.new(1582, 10, 4, 23) + 1.hour
# => Fri, 15 Oct 1582 00:00:00 +0000
15 Time
への拡張
15.1 計算
これらは類似しています。上記のドキュメントを参照して、以下の違いに注意してください:
change
は追加の:usec
オプションを受け入れます。Time
はDSTを理解しているため、正しいDSTの計算が得られます。たとえば、
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
# バルセロナでは、2010/03/28 02:00 +0100はDSTのために2010/03/28 03:00 +0200になります。
t = Time.local(2010, 3, 28, 1, 59, 59)
# => Sun Mar 28 01:59:59 +0100 2010
t.advance(seconds: 1)
# => Sun Mar 28 03:00:00 +0200 2010
15.1.1 Time.current
Active Supportは、Time.current
を現在のタイムゾーンの今日と定義しています。これはTime.now
と似ていますが、定義されている場合はユーザーのタイムゾーンを尊重します。また、past?
、today?
、tomorrow?
、next_day?
、yesterday?
、prev_day?
、future?
といったインスタンスの述語も定義されており、すべてTime.current
に対して相対的です。
ユーザーのタイムゾーンを尊重するメソッドを使用して時間の比較を行う場合は、Time.now
の代わりにTime.current
を使用してください。ユーザーのタイムゾーンがシステムのタイムゾーンよりも未来にある場合、デフォルトでTime.now
が使用するシステムのタイムゾーンと比較して、Time.now.to_date
はDate.yesterday
と等しくなる可能性があります。
注意:active_support/core_ext/time/calculations.rb
で定義されています。
15.1.2 all_day
、all_week
、all_month
、all_quarter
、all_year
メソッドall_day
は、現在の時間の一日全体を表す範囲を返します。
now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_day
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Mon, 09 Aug 2010 23:59:59 UTC +00:00
同様に、all_week
、all_month
、all_quarter
、all_year
は、時間範囲を生成するための目的で使用されます。
now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_week
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:59:59 UTC +00:00
now.all_week(:sunday)
# => Sun, 16 Sep 2012 00:00:00 UTC +00:00..Sat, 22 Sep 2012 23:59:59 UTC +00:00
now.all_month
# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:59:59 UTC +00:00
now.all_quarter
# => Thu, 01 Jul 2010 00:00:00 UTC +00:00..Thu, 30 Sep 2010 23:59:59 UTC +00:00
now.all_year
# => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:59:59 UTC +00:00
注意:active_support/core_ext/date_and_time/calculations.rb
で定義されています。
15.1.3 prev_day
、next_day
prev_day
と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
で定義されています。
15.1.4 prev_month
、next_month
prev_month
とnext_month
は、前月または翌月の同じ日の時間を返します:
ruby
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
で定義されています。
15.1.5 prev_year
, next_year
prev_year
と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
で定義されています。
15.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
で定義されています。
15.2 時間のコンストラクタ
Active Supportでは、ユーザーのタイムゾーンが定義されている場合はTime.zone.now
、それ以外の場合はTime.now
となるTime.current
が定義されています。
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
Time.current
# => Fri, 06 Aug 2010 17:11:58 CEST +02:00
DateTime
と同様に、述語past?
とfuture?
はTime.current
に対して相対的です。
構築する時間がランタイムプラットフォームでサポートされている範囲を超える場合、マイクロ秒は破棄され、代わりにDateTime
オブジェクトが返されます。
15.2.1 期間
Duration
オブジェクトは、時間オブジェクトに加算または減算することができます。
now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now + 1.year
# => Tue, 09 Aug 2011 23:21:11 UTC +00:00
now - 1.week
# => Mon, 02 Aug 2010 23:21:11 UTC +00:00
これらはsinc
またはadvance
への呼び出しに変換されます。たとえば、ここではカレンダー改革の正しいジャンプが得られます。
Time.utc(1582, 10, 3) + 5.days
# => Mon Oct 18 00:00:00 UTC 1582
16 File
への拡張
16.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
が実行するchmod操作のため、対象のファイルにACLが設定されている場合、このACLは再計算/変更されます。
警告。atomic_write
で追加することはできません。
補助ファイルは一時ファイルのための標準ディレクトリに書き込まれますが、第2引数として任意のディレクトリを渡すこともできます。
注意: active_support/core_ext/file/atomic.rb
で定義されています。
17 NameError
への拡張
Active Support は NameError
に missing_name?
を追加し、例外が引数として渡された名前によって発生したかどうかをテストします。
名前はシンボルまたは文字列として指定できます。シンボルは裸の定数名と比較され、文字列は完全修飾定数名と比較されます。
ヒント: シンボルは :"ActiveRecord::Base"
のように完全修飾定数名を表すことができますので、シンボルの振る舞いは便宜上定義されているものであり、技術的にそうする必要があるわけではありません。
例えば、ArticlesController
のアクションが呼び出された場合、Rails は楽観的に ArticlesHelper
を使用しようとします。ヘルパーモジュールが存在しない場合は問題ありませんので、その定数名に対して例外が発生した場合は無視されるべきです。しかし、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
で定義されています。
18 LoadError
への拡張
Active Support は LoadError
に is_missing?
を追加します。
is_missing?
メソッドは、特定のファイルによって例外が発生したかどうかをテストします(おそらく ".rb" 拡張子を除く)。
例えば、ArticlesController
のアクションが呼び出された場合、Rails は articles_helper.rb
を読み込もうとしますが、そのファイルが存在しない場合もあります。それは問題ありません。ヘルパーモジュールは必須ではないため、Rails は読み込みエラーを無視します。しかし、ヘルパーモジュールが存在し、さらに別のライブラリが不足している場合もあります。その場合、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
で定義されています。
19 Pathname への拡張
19.1 existence
existence
メソッドは、指定されたファイルが存在する場合はレシーバーを返し、存在しない場合は nil
を返します。次のようなイディオムに便利です。
content = Pathname.new("file").existence&.read
注意: active_support/core_ext/pathname/existence.rb
で定義されています。
フィードバック
このガイドの品質向上にご協力ください。
タイポや事実の誤りを見つけた場合は、ぜひ貢献してください。 開始するには、ドキュメントへの貢献セクションを読んでください。
不完全なコンテンツや最新でない情報も見つかるかもしれません。 メインのドキュメントに不足しているドキュメントを追加してください。 修正済みかどうかは、まずEdge Guidesを確認してください。 スタイルと規約については、Ruby on Rails Guides Guidelinesを確認してください。
修正すべき点を見つけたが、自分で修正できない場合は、 問題を報告してください。
そして最後に、Ruby on Railsのドキュメントに関するあらゆる議論は、公式のRuby on Railsフォーラムで大歓迎です。