edge
詳細はrubyonrails.orgで: もっとRuby on Rails

Active Supportコア拡張機能

Active Supportは、Ruby言語の拡張とユーティリティを提供するRuby on Railsのコンポーネントです。

これは、Railsアプリケーションの開発とRuby on Rails自体の開発の両方を対象とした、より豊かな言語レベルの機能を提供します。

このガイドを読むことで、以下のことがわかります:

1 コア拡張機能のロード方法

1.1 スタンドアロンのActive Support

Active Supportは、デフォルトで可能な限り最小の依存関係をロードするために、最小限の依存関係をロードします。それは小さな部分に分割されているため、必要な拡張機能のみをロードすることができます。また、関連する拡張機能を一度にロードするための便利なエントリーポイントもあります。

したがって、次のような単純なrequireの後には、Active Supportフレームワークで必要な拡張機能のみがロードされます。

require "active_support"

1.1.1 定義の選択

この例では、Hash#with_indifferent_accessをロードする方法を示しています。この拡張機能は、HashActiveSupport::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アプリケーションでは、次の値が空と見なされます:

  • nilfalse

  • 空白のみで構成された文字列(以下の注意事項を参照)、

  • 空の配列とハッシュ、および

  • 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

警告:任意のクラスはdupcloneを削除するか、それらから例外を発生させることで複製を禁止することができます。したがって、与えられた任意のオブジェクトが複製可能かどうかは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を使用することです。trynilに送信された場合にnilを返す点を除いて、Object#public_sendと似ています。

以下に例を示します。

# tryを使用しない場合
unless @number.nil?
  @number.next
end

# tryを使用する場合
@number.try(:next)

もう1つの例は、ActiveRecord::ConnectionAdapters::AbstractAdapterのコードで、@loggernilの場合があります。コードは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には、DateTimeのように振る舞い、この契約に従うクラスがあります。

注意: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のいくつかのクラスはこのメソッドを上書きしています。

例えば、niltruefalseはそれ自体を返します。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メソッドは、指定されたkeyto_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よりも優れた実装を提供します。これは、HashProcess::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_readerattr_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_readermattr_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_parentObjectを返します。

警告:この場合、module_parent_namenilを返すことに注意してください。

注: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_namenilを返します。

警告:この場合、module_parentObjectを返します。

注: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_writerfalseに設定すると、ライターインスタンスメソッドの生成を防ぐことができます。

module ActiveRecord
  class Base
    class_attribute :table_name_prefix, instance_writer: false, default: "my"
  end
end

モデルは、その属性を設定するための方法として、そのオプションを有用とする場合があります。

オプション:instance_readerfalseに設定すると、リーダーインスタンスメソッドの生成を防ぐことができます。

class A
  class_attribute :x, instance_reader: false
end

A.new.x = 1
A.new.x # NoMethodError

便宜上、class_attributeは、インスタンスリーダーメソッドが返すものの否定の否定であるインスタンス述語も定義します。上記の例では、それはx?と呼ばれるでしょう。

:instance_readerfalseの場合、インスタンス述語はリーダーメソッドと同様にNoMethodErrorを返します。

インスタンス述語を使用しない場合は、instance_predicate: falseを渡して定義しないようにすることができます。

注意:active_support/core_ext/class/attribute.rbで定義されています。

4.1.2 cattr_readercattr_writer、およびcattr_accessor

マクロcattr_readercattr_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_readerfalseに設定することで防止することができます。また、ライターインスタンスメソッドの生成は、:instance_writerfalseに設定することで防止することができます。両方のメソッドの生成を防止するには、:instance_accessorfalseに設定します。いずれの場合も、値は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_accessorfalseに設定することが便利である場合があります。

注意: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!」の場合、出力は正しく形成されないからです。なぜなら、アンパサンドは「&amp;」としてエスケープする必要があるからです。さらに、アプリケーションによっては、ユーザーが手作りのレビュータイトルを設定して悪意のある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 + "<" # => "&lt;"

安全な引数は直接追加されます。

"".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 変換

一般的なルールとして、文字列を変更する可能性があるメソッドは、安全でない文字列を返します。これには、downcasegsubstripchompunderscoreなどがあります。

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: '&hellip;')
# => "Oh dear! Oh &hellip;"

特に、切り詰めは省略文字列の長さも考慮します。

自然な区切りで文字列を切り詰めるには、: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: '&hellip;')
# => "Oh dear! Oh dear!&hellip;"

自然な区切りで文字列を切り詰めるには、: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です。

camelizeunderscoreの逆と考えることができますが、その逆が成り立たない場合もあります。例えば、"SSLError".underscore.camelize"SslError"を返します。このような場合をサポートするために、Active Supportではconfig/initializers/inflections.rbで略語を指定することができます。

ActiveSupport::Inflector.inflections do |inflect|
  inflect.acronym 'SSL'
end

"SSLError".underscore.camelize # => "SSLError"

camelizecamelcaseとしてエイリアスされています。

注意: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]で取得できる値です。

underscorecamelizeの逆と考えることができますが、その逆が成り立たない場合もあります。例えば、"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"

titleizetitlecaseとしてエイリアスされています。

注意: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 # => ""

注意: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_nowagoなどと組み合わせることができます。例えば:

# 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"]

excludingwithoutのエイリアスです。

注意: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 ] ]

メソッドsecondthirdfourthfifthは、対応する要素を返します。second_to_lastthird_to_lastfirstlastは組み込み)も同様です。社会的な知恵と建設的な構築力のおかげで、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"?>

4583

Aaron Batalion

53

aaron-batalion

Joshua Peek

2009-09-02T16:44:36Z

origin/master

2009-09-02T16:44:36Z

Joshua Peek

190316

false

Kill AMo observing wrap_with_notifications since ARes was only using it

723a47bfb3708f968821bc969a9a3fc873a3ed58


もしレシーバがハッシュの配列である場合、ルート要素はデフォルトで "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>

これを行うために、メソッドはペアをループし、値に応じてノードを構築します。ペアkeyvalueが与えられた場合:

  • valueがハッシュの場合、key:rootとして再帰呼び出しを行います。

  • valueが配列の場合、key:rootkeyの単数形を:childrenとして再帰呼び出しを行います。

  • valueが呼び出し可能なオブジェクトの場合、1つまたは2つの引数を受け取る必要があります。引数の数に応じて、呼び出し可能オブジェクトはoptionsハッシュを最初の引数として、key:root、単数形のkeyを2番目の引数として呼び出されます。その戻り値は新しいノードになります。

  • valueto_xmlに応答する場合、メソッドはkey:rootとして呼び出されます。

  • それ以外の場合、keyをタグとするノードが作成され、valueの文字列表現がテキストノードとして追加されます。valuenilの場合、属性"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_mergereverse_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_mergedeep_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 exceptexcept!

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_keysstringify_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_keysdeep_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_keyssymbolize_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_keysdeep_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_optionsto_options!

メソッドto_optionsto_options!は、それぞれsymbolize_keyssymbolize_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_valuesdeep_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.yesterdayDate.tomorrow、およびインスタンスの述語past?today?tomorrow?next_day?yesterday?prev_day?future?on_weekday?on_weekend?も定義されています。これらはすべてDate.currentに対して相対的です。

ユーザータイムゾーンを尊重するメソッドを使用して日付の比較を行う場合は、Date.todayではなくDate.currentを使用してください。ユーザータイムゾーンがシステムタイムゾーンよりも未来にある場合、Date.todayDate.yesterdayと等しくなる可能性があるためです。

注意:active_support/core_ext/date/calculations.rbで定義されています。

13.1.2 名前付き日付

13.1.2.1 beginning_of_weekend_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_weekat_beginning_of_weekにエイリアスされ、end_of_weekat_end_of_weekにエイリアスされています。

注意:active_support/core_ext/date_and_time/calculations.rbで定義されています。

13.1.2.2 monday, sunday

mondaysundayメソッドは、それぞれ前の月曜日と次の日曜日の日付を返します。

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_weeklast_weekにエイリアスされています。

next_weekprev_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_monthend_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_monthat_beginning_of_monthにエイリアスされ、end_of_monthat_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_quarterend_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_quarterat_beginning_of_quarterにエイリアスされ、end_of_quarterat_end_of_quarterにエイリアスされています。

注意:active_support/core_ext/date_and_time/calculations.rbで定義されています。

13.1.2.6 beginning_of_year, end_of_year

beginning_of_yearend_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_yearat_beginning_of_yearにエイリアスされ、end_of_yearat_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_agomonths_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です。このメソッドは、yearsmonthsweeksdaysというキーを持つハッシュを受け取り、現在のキーが示すだけ進んだ日付を返します。

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_dayend_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_dayat_beginning_of_daymidnightat_midnightとしてエイリアスされています。

注意:active_support/core_ext/date/calculations.rbで定義されています。

13.1.6.2 beginning_of_hourend_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_hourat_beginning_of_hourとしてエイリアスされています。

注意:active_support/core_ext/date_time/calculations.rbで定義されています。

13.1.6.3 beginning_of_minuteend_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_minuteat_beginning_of_minuteとしてエイリアスされています。

INFO:beginning_of_hourend_of_hourbeginning_of_minuteend_of_minuteは、Dateインスタンスでは時や分の始まりや終わりを要求する意味がないため、TimeDateTimeに対して実装されていますが、Dateには実装されていません。

注意:active_support/core_ext/date_time/calculations.rbで定義されています。

13.1.6.4 agosince

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 計算

クラスDateTimeDateのサブクラスなので、active_support/core_ext/date/calculations.rbをロードすることでこれらのメソッドとそのエイリアスを継承しますが、常に日時を返します。

以下のメソッドは再実装されているため、これらのメソッドについてはactive_support/core_ext/date/calculations.rbをロードする必要はありません。

一方、advancechangeはより多くのオプションをサポートしており、以下で説明されています。

以下のメソッドはDateTimeインスタンスと一緒に使用する場合にのみ実装されています。

14.1.1 名前付き日時

14.1.1.1 DateTime.current

Active SupportはDateTime.currentTime.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です。このメソッドは、yearsmonthsweeksdayshoursminutessecondsというキーを持つハッシュを受け取り、それに応じて進められた日時を返します。

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#advanceyearsmonthsweeksdaysを渡して目的の日付を計算します。その後、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を使用すると、与えられたオプション(yearmonthdayhourminsecoffsetstart)を除いて、受信者と同じ日時を持つ新しい日時を取得できます。

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
  • sinceまたはagoTimeで表現できない時間にジャンプする場合、代わりにDateTimeオブジェクトが返されます。

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_dateDate.yesterdayと等しくなる可能性があります。

注意:active_support/core_ext/time/calculations.rbで定義されています。

15.1.2 all_dayall_weekall_monthall_quarterall_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_weekall_monthall_quarterall_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_daynext_day

prev_daynext_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_monthnext_month

prev_monthnext_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_yearnext_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_quarternext_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_quarterlast_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 は NameErrormissing_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 は LoadErroris_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フォーラムで大歓迎です。